Skip to main content
Next.js uses file-system based routing — folders and files inside the app directory define your routes. This guide walks through how to create pages, layouts, nested routes, dynamic segments, and links.

Creating a page

A page is UI rendered on a specific route. Create a page by adding a page file inside the app directory and default-exporting a React component.
export default function Page() {
  return <h1>Hello Next.js!</h1>
}

Creating a layout

A layout is UI shared between multiple pages. On navigation, layouts preserve state, remain interactive, and do not re-render. Create a layout by default-exporting a React component from a layout file. The component must accept a children prop, which will receive a page or another nested layout.
export default function DashboardLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="en">
      <body>
        {/* Layout UI */}
        <main>{children}</main>
      </body>
    </html>
  )
}
The layout at app/layout.tsx is the root layout. It is required and must contain <html> and <body> tags.

Creating a nested route

A nested route is a route composed of multiple URL segments. For example, /blog/[slug] is composed of three segments:
  • / (Root Segment)
  • blog (Segment)
  • [slug] (Leaf Segment)
In Next.js:
  • Folders define the route segments that map to URL segments.
  • Files like page and layout define the UI shown for a segment.
To add a route for /blog, create a blog folder inside app, then add a page.tsx file inside it:
import { getPosts } from '@/lib/posts'
import { Post } from '@/ui/post'

export default async function Page() {
  const posts = await getPosts()

  return (
    <ul>
      {posts.map((post) => (
        <Post key={post.id} post={post} />
      ))}
    </ul>
  )
}

Nesting layouts

Layouts in the folder hierarchy are nested by default — they wrap child layouts via their children prop. To create a layout for /blog, add a layout file inside the blog folder:
export default function BlogLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return <section>{children}</section>
}
With both layouts in place, the root layout (app/layout.js) wraps the blog layout (app/blog/layout.js), which in turn wraps the blog index page (app/blog/page.js) and every blog post page (app/blog/[slug]/page.js).

Creating a dynamic segment

Dynamic segments let you generate routes from data. Wrap a folder name in square brackets to create a dynamic segment: [segmentName]. For example, app/blog/[slug]/page.tsx creates a dynamic route for each blog post where [slug] is the dynamic parameter:
export default async function BlogPostPage({
  params,
}: {
  params: Promise<{ slug: string }>
}) {
  const { slug } = await params
  const post = await getPost(slug)

  return (
    <div>
      <h1>{post.title}</h1>
      <p>{post.content}</p>
    </div>
  )
}
To pre-generate static pages for a dynamic segment at build time, export generateStaticParams from the page. This avoids falling back to dynamic rendering at request time.

Rendering with search params

In a Server Component page, you can read search parameters from the URL using the searchParams prop:
export default async function Page({
  searchParams,
}: {
  searchParams: Promise<{ [key: string]: string | string[] | undefined }>
}) {
  const filters = (await searchParams).filters
}
Using searchParams opts your page into dynamic rendering because it requires an incoming request to read the search parameters. Client Components can read search params using the useSearchParams hook instead.

When to use each approach

Use caseApproach
Load data based on search params (pagination, DB filtering)searchParams prop in Server Component
Filter a list already loaded via props (client-only)useSearchParams hook in Client Component
Read params in event handlers without re-rendersnew URLSearchParams(window.location.search)

Linking between pages

Use the <Link> component to navigate between routes. <Link> is a built-in Next.js component that extends the HTML <a> tag to provide prefetching and client-side navigation.
import Link from 'next/link'

export default async function Post({ post }) {
  const posts = await getPosts()

  return (
    <ul>
      {posts.map((post) => (
        <li key={post.slug}>
          <Link href={`/blog/${post.slug}`}>{post.title}</Link>
        </li>
      ))}
    </ul>
  )
}
<Link> is the primary way to navigate between routes. You can also use the useRouter hook for more advanced navigation patterns.

Route Props Helpers

Next.js exposes globally-available utility types that infer params and named slots from your route structure — no imports required. They are generated when running next dev, next build, or next typegen.
  • PageProps — props for page components, including params and searchParams.
  • LayoutProps — props for layout components, including children and any named slots.
app/blog/[slug]/page.tsx
export default async function Page(props: PageProps<'/blog/[slug]'>) {
  const { slug } = await props.params
  return <h1>Blog post: {slug}</h1>
}
app/dashboard/layout.tsx
export default function Layout(props: LayoutProps<'/dashboard'>) {
  return (
    <section>
      {props.children}
      {/* If you have app/dashboard/@analytics, it appears as a typed slot */}
      {/* {props.analytics} */}
    </section>
  )
}
Static routes resolve params to {}. Types are generated during next dev, next build, or next typegen.

Build docs developers (and LLMs) love