Project structure
The App Router, introduced in Next.js 13, is built around a file-based routing system where folders represent URL segments and special files define how each route behaves. Instead of configuring routes manually, you colocate UI, loading states, error handling, and backend logic directly inside the route folder. This approach makes applications easier to reason about, scale, and maintain.
page.tsx
The page file represents the entry point of a route and is responsible for rendering the main UI associated with a specific URL. Every accessible route in the App Router must contain a page file; without it, the route will not be exposed. When a user navigates to a URL, Next.js renders the closest matching page file within the folder hierarchy. This file defines what the user actually sees when visiting that route.
- Every publicly accessible route must have a
page.tsx - Without it, the route does not exist
layout.tsx
The layout file defines shared UI that wraps all child routes within the same folder. It is commonly used for elements such as headers, navigation bars, sidebars, footers, and global providers. One of the most important characteristics of a layout is that it persists across navigations, meaning it does not re-render when moving between child pages. This makes layouts ideal for stable UI elements and performance-sensitive wrappers.
template.tsx
The template file is similar to a layout in structure and purpose, but it behaves differently during navigation. Unlike layouts, templates are re-created and re-rendered on every route change. This makes them useful in situations where UI state must be reset when navigating, such as restarting animations, resetting forms, or reinitializing local state that should not persist between pages.
loading.tsx
The loading file defines a loading or skeleton UI that is automatically shown while a route is being prepared. This includes scenarios such as data fetching, streaming server components, or waiting for asynchronous operations. Next.js internally wraps the page with React Suspense, making loading states declarative and effortless. By providing a loading file, you improve perceived performance and give users immediate visual feedback.
error.tsx
The error file acts as a route-level error boundary, catching runtime errors that occur while rendering the route or its child components. When an error happens, this file renders a fallback UI instead of crashing the entire application. Error files must be Client Components and receive useful props such as the error itself and a reset function, which allows retrying the rendering process without a full page reload.
global-error.tsx
The global-error file is a special error boundary that applies to the entire application. It is used as a final safety net when an error is not caught by any route-level error boundary. This file lives at the root of the app directory and is particularly useful for handling catastrophic or unexpected failures that would otherwise break the whole app.
not-found.tsx
The not-found file defines the UI shown when a route cannot be resolved or when missing data explicitly triggers a not-found state. It is rendered when the user navigates to a non-existent route or when the notFound() function is called from within a page or layout. This allows developers to create a custom 404 experience that feels integrated with the rest of the application.
route.ts
The route file is used to create backend API endpoints directly inside the App Router. It replaces the traditional pages/api approach and uses the standard Web Fetch API. Each route file can export functions corresponding to HTTP methods such as GET, POST, PUT, and DELETE. This allows frontend and backend logic to live side by side, improving cohesion and discoverability.
default.tsx
The default file provides a fallback UI for parallel routes when a specific slot does not have content to render. It is mainly used in advanced routing scenarios such as dashboards, modals, or multi-pane layouts. When a parallel route is missing or inactive, Next.js renders the default file to ensure the UI remains consistent and complete.
Mental Model
A helpful way to understand the App Router is to think of folders as URLs and files as behaviors. The page defines what is rendered, the layout defines what wraps that content, loading controls what appears while waiting, error handles failures, and route defines backend logic. Together, these conventions form a declarative and predictable routing system.
| File | Responsibility |
|---|---|
page.tsx | What is rendered |
layout.tsx | What wraps the content |
template.tsx | What resets on navigation |
loading.tsx | What shows while waiting |
error.tsx | What happens on failure |
route.ts | Backend logic |
Dynamic routesβ
Dynamic routes allow URLs to contain variables, such as IDs or slugs. They are defined using square brackets.
Basic dynamic route
app/blog/[slug]/page.tsx
- /blog/hello-world
- /blog/nextjs-routing
export default function Page({ params }: { params: { slug: string } }) {
return <h1>Post: {params.slug}</h1>
}
Multiple dynamic segments
app/shop/[category]/[product]/page.tsx
export default function Page({ params }: { params: { category: string; product: string }}) {
return (
<div>
<p>Category: {params.category}</p>
<p>Product: {params.product}</p>
</div>
)
}
Catch-all routes
app/docs/[...slug]/page.tsx
export default function Page({ params }: { params: { slug: string[] }}) {
return <pre>{JSON.stringify(params.slug)}</pre>
}
Route groupsβ
Route groups allow you to organize routes and share layouts without affecting the URL structure. They are created by wrapping a folder name in parentheses.
app/(marketing)/page.tsx
(marketing) is omitted from the URL
Private folderβ
Private folders are used to colocate non-routable files alongside routes. They are identified by a leading underscore (_).
app/blog/_components/Post.tsx
app/blog/_lib/data.ts
These folders are never routable