When learning Next.js routing today, start with the App Router. The Pages Router still exists, especially in older projects, but new tutorials, new applications, and most current framework patterns are centered on the app/ directory.
The core model is straightforward: folders describe URL segments, page.tsx creates a route, and layout.tsx wraps routes below it.
Basic App Router structure
app/
page.tsx -> /
about/
page.tsx -> /about
blog/
[slug]/
page.tsx -> /blog/:slug
The main differences from older examples:
- Pages live in
app/**/page.tsx. - Layouts are persistent wrappers defined by
layout.tsx. - Server Components are the default in App Router.
For component boundary decisions, read Next.js Server vs Client Components.
Static routes
A fixed route is just a folder with a page file:
app/
pricing/
page.tsx
The URL is /pricing:
export default function PricingPage() {
return <h1>Pricing</h1>
}
This is the right model for landing pages, about pages, tools, dashboards, and most top-level product sections.
Dynamic routes
Use square brackets when part of the path is a variable:
app/blog/[slug]/page.tsx
In current Next.js versions, params is commonly typed and awaited as a promise:
export default async function Page({
params,
}: {
params: Promise<{ slug: string }>
}) {
const { slug } = await params
return <div>{slug}</div>
}
Older tutorials often show params as a synchronous object. Check the version and documentation before copying code into a new app.
Catch-all routes
[...slug] matches multiple path segments:
app/docs/[...slug]/page.tsx
It can match:
/docs/setup
/docs/react/server-components
The value becomes an array. If the route should also match the parent path itself, use optional catch-all syntax:
app/docs/[[...slug]]/page.tsx
That can match both /docs and /docs/setup.
Layouts are not just wrapper components
Layouts persist across route transitions under the same segment:
app/
dashboard/
layout.tsx
page.tsx
analytics/
page.tsx
/dashboard and /dashboard/analytics share the dashboard layout. This is useful for:
- Dashboard navigation
- Documentation sidebars
- Account settings shells
- Multi-section tools
If a layout needs local loading and error boundaries, add files such as loading.tsx and error.tsx in the right segment.
Route Groups
Route Groups use parentheses:
app/
(marketing)/
about/
page.tsx
The group name does not appear in the URL, so the final path is still /about.
Use groups to organize code by business area, layout family, or team ownership. Be careful not to create conflicting URLs from different groups. Also remember that switching between different root layouts may trigger a full page load.
Parallel Routes
Parallel Routes use named slots:
app/
dashboard/
layout.tsx
@team/
page.tsx
@analytics/
page.tsx
The layout receives those slots as props:
export default function Layout({
children,
team,
analytics,
}: {
children: React.ReactNode
team: React.ReactNode
analytics: React.ReactNode
}) {
return (
<>
{children}
{team}
{analytics}
</>
)
}
This is an advanced pattern for dashboards, deep-linkable modals, or independently loading panes. A normal blog, marketing site, or CRUD interface usually does not need it.
Route Handlers
API routes in App Router live in app/api/**/route.ts:
export async function GET() {
return Response.json({ ok: true })
}
Route Handlers are useful for lightweight APIs, webhooks, form endpoints, and server-side adapters that belong inside the Next.js app.
If you are choosing between Route Handlers, client fetch, Server Actions, and backend APIs, read Next.js Server Actions vs Fetch: How to Choose.
When Pages Router still appears
You will still see pages/ in older codebases, gradual migrations, and packages built around historical conventions. That does not make those projects wrong. It means the routing model must be read in context.
For new pages and new learning material, prefer the App Router model so the code does not become obsolete quickly.
Related FreeMac guides
- For images in App Router pages, read Next.js Image Guide: sizes, fill, and Remote Images.
- For hydration problems, see React Hydration Failed: Causes, Debugging, and Fixes.
- For configuration files around a Next.js project, read Front-End Config: Env, Node, Git, ESLint, and TypeScript.
Continue reading
Next.js Server vs Client Components: How to Choose
Choose between Server Components and Client Components in the Next.js App Router by looking at data access, state, events, browser APIs, serialization, and bundle size.
9 minReact Hydration Failed: Causes, Debugging, and Fixes
Debug server and client rendering mismatches caused by dates, random values, browser APIs, invalid HTML, local storage, locale differences, and DOM-changing extensions.
11 min readNext.js Image Guide: sizes, fill, and Remote Images
Use the modern Next.js Image component with width and height, fill, sizes, preload, placeholders, remotePatterns, and clear rules for avoiding outdated layout props.
Subscribe to FreeMac
Weekly picks: free Mac software reviews, trusted source updates, alternatives, and low-friction guides.