Next.js 16 File Conventions — The Complete Reference
Step 31 of 31 — Next.js Tutorial Series | Source code
What You Will Learn
- Every special file name recognized by the Next.js 16 App Router
- Which files are route-level, app-wide, or project-root conventions
- What changed from earlier Next.js versions (deprecated and renamed files)
- A practical quick-reference table you can bookmark
Table of Contents
- How File Conventions Work
- Route UI Files
- Error & Loading Files
- Not Found Handling
- API Routes
- Dynamic Route Segments
- Route Groups & Parallel Routes
- Intercepting Routes
- Proxy (formerly Middleware)
- Static Metadata Files
- Project Config Files
- Deprecated Conventions
- What You Actually Need
- Summary & Key Takeaways
How File Conventions Work
Next.js 16 uses the App Router. When you create a file with a specific name inside the app/ directory, Next.js automatically assigns it special behavior — no configuration required.
These conventions fall into three categories:
| Category | Examples |
|---|---|
| Route-level | page.tsx, layout.tsx, loading.tsx, error.tsx, not-found.tsx |
| App-wide / Root | app/layout.tsx (root layout), app/not-found.tsx (global 404) |
| Project root | proxy.ts, instrumentation.ts, next.config.ts |
All examples use
.tsx/.ts, but.jsx/.jswork identically.
Route UI Files
page.tsx
Defines the UI for a route. A folder only becomes a publicly accessible route when it contains a page.tsx.
// app/posts/page.tsx → accessible at /posts
export default function PostsPage() {
return <h1>Posts</h1>
}
layout.tsx
Shared UI that wraps child routes. Layouts persist across navigations — they don't remount.
// app/dashboard/layout.tsx
export default function DashboardLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<div>
<nav>Dashboard Nav</nav>
{children}
</div>
)
}
The root layout (app/layout.tsx) is required and must include <html> and <body> tags.
template.tsx
Like layout.tsx but re-renders on every navigation. Use it when you need a fresh component instance each time (e.g. enter/exit animations, resetting state).
// app/dashboard/template.tsx
export default function Template({ children }: { children: React.ReactNode }) {
return <div>{children}</div>
}
default.tsx
Fallback UI for parallel routes when Next.js can't determine which slot to render after a navigation.
// app/@analytics/default.tsx
export default function Default() {
return null
}
Error & Loading Files
error.tsx
Catches runtime errors inside a route segment. Must be a Client Component.
// app/posts/error.tsx
'use client'
export default function Error({
error,
reset,
}: {
error: Error
reset: () => void
}) {
return (
<div>
<p>Something went wrong: {error.message}</p>
<button onClick={reset}>Try again</button>
</div>
)
}
global-error.tsx
Catches errors in the root layout itself. Also must be a Client Component. Must render its own <html> and <body>.
loading.tsx
Displayed automatically while the route's page.tsx is loading. Next.js wraps the page in a <Suspense> boundary using this file as the fallback.
// app/posts/loading.tsx
export default function Loading() {
return <div className="animate-pulse">Loading posts…</div>
}
Not Found Handling
not-found.tsx
Rendered when notFound() is called from next/navigation, or when no route matches.
// app/posts/[id]/not-found.tsx
import Link from 'next/link'
export default function NotFound() {
return (
<div>
<h2>Post not found</h2>
<Link href="/posts">Back to posts</Link>
</div>
)
}
Place it at app/not-found.tsx for a global 404 page, or inside any route segment for a scoped one.
API Routes
route.ts
Defines a serverless API endpoint using HTTP method handlers.
// app/api/posts/route.ts → accessible at /api/posts
import { NextResponse } from 'next/server'
export async function GET() {
return NextResponse.json({ message: 'Hello' })
}
export async function POST(request: Request) {
const body = await request.json()
return NextResponse.json(body, { status: 201 })
}
A folder cannot contain both route.ts and page.tsx.
Dynamic Route Segments
| Convention | URL Example | Params |
|---|---|---|
[id] | /posts/123 | { id: '123' } |
[...slug] | /docs/a/b/c | { slug: ['a', 'b', 'c'] } |
[[...slug]] | /docs or /docs/a/b | { slug: undefined } or { slug: ['a', 'b'] } |
// app/posts/[id]/page.tsx
export default async function PostPage({
params,
}: {
params: Promise<{ id: string }>
}) {
const { id } = await params
return <h1>Post {id}</h1>
}
Next.js 16 change:
paramsis now aPromise— you mustawaitit.
Route Groups & Parallel Routes
Route Groups — (folder)
Organize routes without affecting the URL.
app/(marketing)/about/page.tsx → /about
app/(shop)/products/page.tsx → /products
Use cases: sharing a layout across a subset of routes, keeping the file tree organized.
Parallel Routes — @slot
Render multiple page components simultaneously in the same layout.
app/@analytics/page.tsx
app/@team/page.tsx
app/layout.tsx ← receives { analytics, team } as props
Intercepting Routes
Used for modals and advanced navigation (e.g. opening a photo in a modal while keeping the URL).
| Convention | Intercepts |
|---|---|
(.)segment | Same level |
(..)segment | One level up |
(...)segment | From the root |
app/feed/(.)photo/[id]/page.tsx
This is an advanced pattern — most apps don't need it.
Proxy (formerly Middleware)
proxy.ts (Next.js 15.2+)
The modern replacement for middleware.ts. Located at the project root.
// proxy.ts
import NextAuth from 'next-auth'
import { authConfig } from './auth.config'
export const { auth: middleware } = NextAuth(authConfig)
proxy.ts runs before a request reaches your route. Common uses:
- Authentication — redirect unauthenticated users
- Redirects — map old URLs to new ones
- Headers — add security or CORS headers
What changed: In Next.js versions before 15.2, this file was called
middleware.ts. The old name still works as a fallback, butproxy.tsis the recommended convention going forward. We covered this migration in Step 22.
Static Metadata Files
These are generated at build time from TypeScript/JavaScript files inside app/.
| File | Generates | Example |
|---|---|---|
robots.ts | robots.txt | export default function robots() { ... } |
sitemap.ts | sitemap.xml | export default function sitemap() { ... } |
manifest.ts | manifest.json (PWA) | export default function manifest() { ... } |
opengraph-image.tsx | OG image | Dynamic image generation |
icon.tsx | Favicon | Dynamic icon generation |
You can also use the Metadata API directly in layout.tsx or page.tsx:
// Static metadata
export const metadata = {
title: 'My App',
description: 'Built with Next.js 16',
}
// Dynamic metadata
export async function generateMetadata({ params }) {
const { id } = await params
const post = await getPost(id)
return { title: post.title }
}
Project Config Files
These live at the project root, not inside app/.
| File | Purpose |
|---|---|
next.config.ts | Next.js configuration (redirects, images, env, etc.) |
instrumentation.ts | Observability and tracing hooks (stable since Next.js 15) |
proxy.ts | Request interception (auth, redirects, headers) |
Deprecated Conventions
| Old Convention | Status in Next.js 16 | Modern Replacement |
|---|---|---|
head.tsx | Removed | export const metadata or generateMetadata() in page.tsx / layout.tsx |
middleware.ts | Deprecated (still works) | proxy.ts at project root |
pages/ directory | Legacy (Pages Router) | app/ directory (App Router) |
_app.tsx, _document.tsx | Legacy (Pages Router) | app/layout.tsx |
getServerSideProps, getStaticProps | Legacy (Pages Router) | async Server Components + fetch / Prisma directly |
What You Actually Need
Most real-world Next.js 16 apps use only these files:
| File | Why |
|---|---|
page.tsx | Every route needs one |
layout.tsx | Shared navigation, providers, metadata |
loading.tsx | Instant feedback while data loads |
error.tsx | Graceful error handling |
not-found.tsx | Custom 404 pages |
route.ts | API endpoints (when needed) |
proxy.ts | Authentication and redirects |
The advanced conventions (template.tsx, default.tsx, parallel routes, intercepting routes) are powerful but only needed for specific use cases.
Summary & Key Takeaways
| Concept | Details |
|---|---|
| File-based routing | A folder becomes a route only when it has page.tsx |
| Layouts persist | layout.tsx doesn't remount; template.tsx does |
| Error boundaries | error.tsx (per-segment) and global-error.tsx (root) — both Client Components |
| Loading states | loading.tsx = automatic <Suspense> fallback |
| API routes | route.ts with named HTTP method exports (GET, POST, etc.) |
| Dynamic segments | [id], [...slug], [[...slug]] — params is a Promise in Next.js 16 |
| Route groups | (folder) for organization without URL impact |
| Proxy replaces middleware | proxy.ts is the Next.js 16 convention; middleware.ts is deprecated |
| Metadata API | export const metadata or generateMetadata() — replaces the old head.tsx |
| Config files | next.config.ts, proxy.ts, instrumentation.ts live at project root |