File-based routing and layouts

How folders in app/ become URLs, how nested layouts persist across navigation, and the special files that handle loading and errors.

Folders are routes

In the App Router, a directory under app/ becomes a URL segment, and a page.js inside it makes that segment addressable. Files without an export named default page do not create routes.

Path on diskURLNotes
app/page.js/Home page
app/blog/page.js/blogStatic segment
app/blog/[slug]/page.js/blog/introDynamic segment, one level
app/docs/[...path]/page.js/docs/a/bCatch-all segments
app/shop/[[...filters]]/page.js/shop and /shop/xOptional catch-all
app/(marketing)/about/page.js/aboutRoute group: folder is skipped in the URL
// app/blog/[slug]/page.js
export default async function PostPage({ params }) {
  const { slug } = await params        // Next.js 15: params is a Promise
  const post = await getPost(slug)
  if (!post) notFound()
  return <article>{post.title}</article>
}

// build-time prerender for known slugs
export async function generateStaticParams() {
  const posts = await listPosts()
  return posts.map(p => ({ slug: p.slug }))
}
  • page.js is the route, layout.js wraps the segment and its children, and route.js replaces the page with an HTTP handler for that path.
  • Dynamic segment names must match the folder: [slug] gives params.slug.
  • From Next.js 15, params and searchParams are promises — await them, or read them with React's use() in a client component.
  • notFound() renders the nearest not-found.js and returns the right 404 status.

Nested layouts that survive navigation

// app/layout.js — required root layout
import './globals.css'

export const metadata = { title: { default: 'Guides', template: '%s | Guides' } }

export default function RootLayout({ children }) {
  return (
    <html lang="en">
      <body>
        <SiteHeader />
        <main>{children}</main>
      </body>
    </html>
  )
}
// app/docs/layout.js — wraps every route under /docs
export default function DocsLayout({ children }) {
  return (
    <div className="docs">
      <DocsSidebar />
      {children}
    </div>
  )
}
  • Layouts nest: the root layout wraps everything, and each segment can add its own shell. Only the parts that change re-render on navigation, and layout state is preserved.
  • A layout receives children but never the current route's params from a deeper level — keep per-route work in the page.
  • Use template.js instead of layout.js when you need a fresh instance per navigation (an entrance animation, or a form that resets).
  • Route groups with parentheses let you share a layout across unrelated URLs without putting that segment in the path.

Loading, error and not-found files

// app/blog/loading.js — shown instantly while the page streams
export default function Loading() {
  return <Skeleton rows={5} />
}

// app/blog/error.js — must be a client component
'use client'

export default function Error({ error, reset }) {
  return (
    <div role="alert">
      <p>Could not load posts: {error.message}</p>
      <button onClick={reset}>Try again</button>
    </div>
  )
}
  • loading.js wraps the segment in a Suspense boundary, so the shell renders immediately and the content fills in.
  • error.js catches render and data errors below it; it receives a reset function that retries the segment.
  • not-found.js renders when notFound() is called or a URL matches nothing.
  • global-error.js is the last resort and must render its own html and body elements.
💡
Special files are per segment, not per app: an error.js in app/blog/ does not protect the rest of the site. Add one where a failure is plausible, and rely on the root for anything unexpected.

FAQ

Do I still need the pages/ router?
No for new work. Pages Router still functions and existing apps can stay on it, but new features land in the App Router, and mixing the two in one project is more confusing than migrating.
How do I link between pages without a full reload?
Import Link from next/link. It prefetches visible links in production, so navigation is usually instant.

Data fetching and server components Components and props

Last refreshed 2026-09-18.