Next.jsBeginner

Getting Started with the Next.js App Router

A reference guide to the Next.js App Router: file conventions, layouts, and data fetching.

DevFieldGuideJuly 21, 2026 (updated July 30, 2026)6 min read
Share:

This is a reference-style guide to the core conventions of the Next.js App Router.

File conventions

FilePurpose
layout.tsxShared UI that wraps a segment and its children
page.tsxThe unique UI for a route, making it publicly accessible
loading.tsxLoading UI shown while a segment loads
not-found.tsxUI rendered when notFound() is called
error.tsxError boundary for a segment
route.tsAn API endpoint (Route Handler) for a segment

Nested layouts

Layouts nest automatically based on folder structure. A layout.tsx in app/blog/ wraps every route under /blog, in addition to the root layout.

app/ layout.tsx → wraps everything blog/ layout.tsx → wraps everything under /blog page.tsx → /blog [slug]/ page.tsx → /blog/[slug]

Static params for dynamic routes

For a fully static export, every dynamic segment needs generateStaticParams so Next.js knows every path to pre-render at build time.

tsx
export async function generateStaticParams() {
  const posts = getAllPosts();
  return posts.map((post) => ({ slug: post.slug }));
}

Metadata

Each route can export a generateMetadata function to produce per-page <title>, description, and Open Graph tags at build time.

tsx
export async function generateMetadata({ params }) {
  const { slug } = await params;
  const post = getPostBySlug(slug);
  return { title: post.title, description: post.description };
}

Server vs. Client Components

By default, every component in app/ is a Server Component. Add "use client" at the top of a file only when you need interactivity, browser APIs, or React hooks like useState.

Route groups and parallel routes

Two less obvious file conventions worth knowing once the basics above feel routine:

app/ (marketing)/ layout.tsx → its own layout, applies to pages inside page.tsx → still just "/", the parens don't affect the URL (app)/ dashboard/ page.tsx → "/dashboard"

Parentheses around a folder name ((marketing)) create a route group — it organizes routes and can carry its own layout without adding a segment to the actual URL. This is the tool for giving a marketing site section and an authenticated app section entirely different layouts while both living under the same app/ tree.

app/ @modal/ (.)photo/[id]/page.tsx → intercepted route, rendered as a modal photo/[id]/ page.tsx → the full page, for a direct visit/refresh

Parallel and intercepting routes (the @modal and (.) conventions) let a route render as a modal overlay when navigated to from within the app, while still rendering as a full standalone page on a direct URL visit or refresh — the pattern behind "click a photo thumbnail, it opens as a modal, but the modal's URL is also a real, shareable, fully-rendered page."

Loading and error boundaries are scoped per segment

loading.tsx and error.tsx aren't global — each one only wraps the segment (and its children) where it's placed, which means different parts of a route tree can have independent loading and error states instead of one blanket spinner or error page for the whole app:

app/ dashboard/ loading.tsx → shown while anything under /dashboard is loading error.tsx → catches errors from /dashboard and its children only settings/ loading.tsx → overrides the parent's, just for /dashboard/settings

A nested loading.tsx takes priority over a parent's for its own subtree, letting a slow-loading nested section show its own loading state without blanking out the rest of an already-rendered page around it.

Colocating non-route files safely

Any file inside app/ that isn't one of the special names above (_components/, _utils/, or a leading-underscore folder) is safe to colocate next to the routes that use it, without becoming a route itself:

app/ blog/ _components/ PostCard.tsx → not a route, safe to colocate here page.tsx [slug]/ page.tsx

The underscore prefix explicitly opts a folder out of routing, which is the cleanest way to keep a route's helper components physically close to where they're used instead of pushed into a distant shared components/ directory purely to avoid accidentally creating a route.

Common mistakes

Common mistakes
  • Adding "use client" to a component "just in case." Every Client Component adds JavaScript to the bundle sent to the browser — reserve it for components that genuinely need interactivity, and keep data-fetching and static markup as Server Components.
  • Calling a browser-only API (window, localStorage, document) directly during render in a Client Component. It runs during the server-render pass too (Client Components are still rendered on the server for the initial HTML) — guard with a mount check or move the read into a useEffect.
  • Forgetting generateStaticParams on a dynamic route ([slug]) when building with output: "export". Static export has no server to render a route on demand — every dynamic segment must be pre-rendered at build time, or the build fails outright rather than falling back gracefully.
  • Fetching data in a Client Component with useEffect when the same data could be fetched in a parent Server Component and passed down as a prop — this needlessly moves the fetch to the browser, adding a loading state and a network waterfall that server-side fetching avoids entirely.

Troubleshooting

Best practices
  • Hydration mismatch warnings — check for any value that can differ between server and client renders: dates formatted with the visitor's local timezone, random IDs generated without a stable seed, or conditional rendering based on typeof window.
  • "Error: Dynamic server usage" during a static export build — a Server Component is calling something that requires per-request data (reading cookies, headers, or search params) that static export can't resolve at build time. Either remove the dependency or handle that logic client-side.
  • A route renders correctly in next dev but 404s after next build with static export — confirm generateStaticParams actually returns that specific path; dev mode renders dynamic routes on demand, but static export only ships what was explicitly pre-rendered.
Advertisement

Frequently Asked Questions

Advertisement
DevFieldGuide
DevFieldGuide

Editorial Team

Practical tutorials and developer tools, written and maintained by the DevFieldGuide team.

Enjoyed this article?

Get the next one straight to your inbox, along with the best of what we publish each week.

Related Articles

More in Next.js

View all