C.W.K.
Stream
Lesson 04 of 07 · published

Next.js 16: App Router + TS

~9 min · frameworks, nextjs, next-16, app-router

Level 0Curious
0 XP0/93 lessons0/23 achievements
0/100 XP to next level100 XP to go0% complete
"Next.js 16 fully embraces React 19 Server Components. TypeScript follows naturally."

The App Router structure

Next 16's App Router (the only router for new projects) uses file-based routing under app/. app/page.tsx is the root page. app/posts/[id]/page.tsx is a dynamic route. app/layout.tsx wraps children. Each file exports either a default component (page/layout) or specific named exports (loading, error, not-found).

Route segment props are typed automatically

Page and layout components receive typed props from the router. export default async function Page({ params, searchParams }: { params: Promise<{ id: string }>; searchParams: Promise<{ q?: string }> }). Note that in Next 16, both params and searchParams are async — you await them before reading. This is part of React's broader async-first model.

Data fetching and revalidation

In Server Components, you fetch directly: const data = await fetch('https://api.example.com/posts').then(r => r.json()). Next caches automatically; pass { next: { revalidate: 60 } } for ISR-style invalidation. The cache and types both flow through TypeScript correctly.

Next.js 16 is React 19 with conventions and infrastructure. Once you understand React 19's Server Components + Actions model, Next.js mostly stays out of your way.

Code

Next.js 16 App Router — page, layout, loading·tsx
// app/posts/[id]/page.tsx
export default async function PostPage({
  params,
}: {
  params: Promise<{ id: string }>;
}) {
  const { id } = await params;     // Next 16 — params is async
  const post = await fetch(`https://api.example.com/posts/${id}`).then((r) => r.json());

  return (
    <article>
      <h1>{post.title}</h1>
      <div dangerouslySetInnerHTML={{ __html: post.body }} />
    </article>
  );
}

// app/layout.tsx
export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html>
      <body>{children}</body>
    </html>
  );
}

// app/posts/loading.tsx — Suspense fallback for /posts and its children.
export default function Loading() {
  return <div>Loading posts…</div>;
}

External links

Exercise

Set up a small Next.js 16 app with a /posts listing page and a /posts/[id] detail page. Both should fetch from a mock API (JSONPlaceholder or your own). Confirm the params are typed and the await works in async server components.
Hint
Use create-next-app to scaffold. The page components default to Server Components. The params object is Promise<{ id: string }> — await it before accessing .id.

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue

Comments 0

🔔 Reply notifications (sign in)
Sign inPlease sign in to comment.

No comments yet — be the first.