All notes
Web & Framework3 min readUpdated August 7, 2026

Building Systems with Next.js (App Router)

How modern Next.js splits work between server and browser, plus several version 16 rules that differ from the older tutorials still circulating.

Next.jsReactApp RouterServer ComponentTypeScript

Next.js is a React framework that adds routing, server rendering and a build pipeline. Its biggest departure from plain React: some components run on the server and are never shipped to the browser at all.

Folders are routes

FileRole
app/page.tsxThe page at /
app/knowledge/page.tsxThe page at /knowledge
app/knowledge/[slug]/page.tsxA dynamic segment matching /knowledge/anything
app/layout.tsxThe frame wrapping the pages beneath it
app/loading.tsxWhat shows while data is still coming
app/not-found.tsxThe 404 view
app/api/…/route.tsAn HTTP endpoint rather than a page

Server Components and Client Components

Every component is a Server Component by default. It runs on the server, may touch a database and secrets, and only its rendered output reaches the browser. The moment a component needs state, an event handler, or a browser API, mark it with 'use client' on the first line.

The part people miss: 'use client' is not a per-component label, it is a boundary. Everything that file imports joins the browser bundle. So push the directive as deep as possible — onto the button or form that is genuinely interactive, not onto the layout that wraps everything.

A Server Component fetches; a Client Component handles interaction
// app/visitors/[id]/page.tsx  — Server Component
export default async function Page(props: PageProps<'/visitors/[id]'>) {
  const { id } = await props.params;      // params is a Promise since v15
  const visitor = await getVisitor(id);   // credentials stay on the server

  return <CheckInButton visitorId={visitor.id} />;
}

// app/ui/check-in-button.tsx
'use client';

export default function CheckInButton({ visitorId }: { visitorId: number }) {
  const [loading, setLoading] = useState(false);
  // ...
}

Note the awaited params. Since Next.js 15, params and searchParams are Promises. Older code that reads params.id directly will fail on this version — one of the most common errors when following an out-of-date tutorial.

What changed about caching in 16

  • fetch is no longer cached by default. It used to be, and plenty of tutorials still teach the old behaviour.
  • To keep a result, use the 'use cache' directive on the function or component, paired with cacheLife to set its lifetime.
  • The Cache Components model is switched on with cacheComponents: true in next.config.ts.
  • Pages whose data never changes are rendered once at build time — like the notes you are reading.

Changing data with a Server Action

Saving data does not require building your own API endpoint. An async function marked 'use server' can be called straight from a form, and Next.js handles the request. The consequence: that function can be POSTed to directly from outside the app, so authentication still has to be checked inside it.

More notes