Home Quizzes Leaderboard Competitions Learn Hire Us
About Contact
Log In Sign Up
Learn Next.js File-Based Routing

File-Based Routing

⏱ 12 min read read
File-Based Routing Rules:

app/page.tsx → /

app/about/page.tsx → /about

app/blog/page.tsx → /blog

app/blog/[slug]/page.tsx → /blog/:slug (dynamic)

app/shop/[...slug]/page.tsx → /shop/a/b/c (catch-all)

app/(auth)/login/page.tsx → /login (group --- (auth) not in URL)

Special Files:

page.tsx ← the route UI (makes a URL segment public)

layout.tsx ← wraps page and all children; persists across navigations

loading.tsx ← automatic loading UI (Suspense boundary)

error.tsx ← automatic error boundary for the segment

not-found.tsx ← rendered when notFound() is called

route.ts ← API endpoint (no UI)

Dynamic Routes:

// app/blog/[slug]/page.tsx

// Receives { params: { slug: string } }

export default function BlogPost({

params,

}: {

params: { slug: string };

}) {

return <h1>Post: {params.slug}</h1>;

}

Layouts --- Persist Across Navigation:

Layouts wrap pages and do NOT re-render when navigating between child
pages. Perfect for nav bars, sidebars, and shells.

// app/dashboard/layout.tsx

export default function DashboardLayout({

children,

}: {

children: React.ReactNode;

}) {

return (

<div className="dashboard">

<Sidebar />

<main>{children}</main>

</div>

);

}

Route Groups (auth) --- wrap routes in a folder with () to share a
layout

without adding the folder name to the URL.

app/(marketing)/page.tsx → /

app/(marketing)/about/page.tsx → /about

app/(auth)/login/page.tsx → /login

Both groups can have their own layout.tsx.
Code Example
// File structure:

// app/

// page.tsx → /

// layout.tsx → root layout

// blog/

// page.tsx → /blog

// [slug]/

// page.tsx → /blog/hello-world

// dashboard/

// layout.tsx → persistent sidebar

// page.tsx → /dashboard

// settings/page.tsx → /dashboard/settings

// (auth)/

// login/page.tsx → /login (no '(auth)' in URL)

// app/blog/[slug]/page.tsx

import { notFound } from 'next/navigation';

type Props = { params: { slug: string } };

export default function BlogPost({ params }: Props) {

const post = getPostBySlug(params.slug); // your data function

if (!post) notFound(); // renders not-found.tsx

return (

<article>

<h1>{post.title}</h1>

<p>{post.content}</p>

</article>

);

}

// Generate static paths at build time

export async function generateStaticParams() {

const posts = await getAllPosts();

return posts.map((p) => ({ slug: p.slug }));

}

// app/dashboard/layout.tsx --- persistent sidebar

export default function DashboardLayout({

children,

}: {

children: React.ReactNode;

}) {

return (

<div style={{ display: 'flex' }}>

<nav style={{ width: 240 }}>

<a href="/dashboard">Home</a>

<a href="/dashboard/settings">Settings</a>

</nav>

<main style={{ flex: 1 }}>{children}</main>

</div>

);

}
← Hello, Next.js! --- Your First App Data Fetching →

Log in to track your progress and earn badges as you complete lessons.

Log In to Track Progress