Next.js Complete Guide Part 2: Mastering File-Based Routing and Navigation

Next.js Complete Guide Part 2: Mastering File-Based Routing and Navigation

Deep dive into Next.js routing system - Learn file-based routing, dynamic routes, nested layouts, navigation, and advanced routing patterns with practical examples.

By Next.js Expert
11 min read
2026 words

Welcome back to our comprehensive Next.js series! In Part 1, we covered the basics and setup. Now we'll dive deep into one of Next.js's most powerful features: the file-based routing system.

Understanding File-Based Routing

Unlike traditional React apps where you manually configure routes, Next.js automatically creates routes based on your file structure in the pages directory. This approach is intuitive, scalable, and reduces boilerplate code.

Basic Routing Examples

pages/
├── index.js          → / (homepage)
├── about.js          → /about
├── contact.js        → /contact
├── blog.js           → /blog
└── services.js       → /services

Key Rules:

  • Files in pages/ become routes automatically
  • index.js files represent the root of a directory
  • File names become URL paths
  • Only .js, .jsx, .ts, .tsx files create routes

Creating Static Routes

Let's build a complete website structure:

1. Homepage (pages/index.js)

import Head from 'next/head'
import Link from 'next/link'
import styles from '../styles/Home.module.css'

export default function Home() {
  return (
    <div className={styles.container}>
      <Head>
        <title>My Next.js Website</title>
        <meta name="description" content="Welcome to my Next.js website" />
      </Head>

      <nav className={styles.nav}>
        <Link href="/">Home</Link>
        <Link href="/about">About</Link>
        <Link href="/services">Services</Link>
        <Link href="/blog">Blog</Link>
        <Link href="/contact">Contact</Link>
      </nav>

      <main className={styles.main}>
        <h1>Welcome to My Website</h1>
        <p>This is the homepage of our Next.js application.</p>
        
        <div className={styles.grid}>
          <Link href="/about" className={styles.card}>
            <h2>About Us &rarr;</h2>
            <p>Learn more about our company and mission.</p>
          </Link>

          <Link href="/services" className={styles.card}>
            <h2>Services &rarr;</h2>
            <p>Discover what we can do for you.</p>
          </Link>

          <Link href="/blog" className={styles.card}>
            <h2>Blog &rarr;</h2>
            <p>Read our latest articles and insights.</p>
          </Link>
        </div>
      </main>
    </div>
  )
}

2. About Page (pages/about.js)

import Head from 'next/head'
import Link from 'next/link'

export default function About() {
  return (
    <div>
      <Head>
        <title>About Us - My Next.js Website</title>
        <meta name="description" content="Learn about our company" />
      </Head>

      <nav>
        <Link href="/">← Back to Home</Link>
      </nav>

      <main>
        <h1>About Us</h1>
        <p>We are a company dedicated to building amazing web applications.</p>
        
        <section>
          <h2>Our Mission</h2>
          <p>To create beautiful, fast, and user-friendly web experiences.</p>
        </section>

        <section>
          <h2>Our Team</h2>
          <p>We have a talented team of developers and designers.</p>
        </section>
      </main>
    </div>
  )
}

Nested Routes with Folders

Create nested routes using folders:

pages/
├── index.js                    → /
├── about.js                    → /about
├── blog/
│   ├── index.js               → /blog
│   ├── getting-started.js     → /blog/getting-started
│   └── advanced-tips.js       → /blog/advanced-tips
└── products/
    ├── index.js               → /products
    ├── laptops.js             → /products/laptops
    └── phones.js              → /products/phones

Blog Section Example

Create pages/blog/index.js:

import Head from 'next/head'
import Link from 'next/link'

export default function Blog() {
  const posts = [
    {
      slug: 'getting-started',
      title: 'Getting Started with Next.js',
      excerpt: 'Learn the basics of Next.js development.',
      date: '2024-01-15'
    },
    {
      slug: 'advanced-tips',
      title: 'Advanced Next.js Tips',
      excerpt: 'Take your Next.js skills to the next level.',
      date: '2024-01-20'
    }
  ]

  return (
    <div>
      <Head>
        <title>Blog - My Next.js Website</title>
        <meta name="description" content="Read our latest blog posts" />
      </Head>

      <nav>
        <Link href="/">← Back to Home</Link>
      </nav>

      <main>
        <h1>Our Blog</h1>
        <p>Stay updated with our latest articles and insights.</p>

        <div className="posts">
          {posts.map(post => (
            <article key={post.slug} className="post-card">
              <h2>
                <Link href={`/blog/${post.slug}`}>
                  {post.title}
                </Link>
              </h2>
              <p>{post.excerpt}</p>
              <time>{post.date}</time>
            </article>
          ))}
        </div>
      </main>
    </div>
  )
}

Create pages/blog/getting-started.js:

import Head from 'next/head'
import Link from 'next/link'

export default function GettingStarted() {
  return (
    <div>
      <Head>
        <title>Getting Started with Next.js - Blog</title>
        <meta name="description" content="Learn the basics of Next.js" />
      </Head>

      <nav>
        <Link href="/blog">← Back to Blog</Link>
      </nav>

      <article>
        <h1>Getting Started with Next.js</h1>
        <time>January 15, 2024</time>
        
        <p>Next.js is a powerful React framework that makes building web applications easier...</p>
        
        <h2>What You'll Learn</h2>
        <ul>
          <li>Setting up a Next.js project</li>
          <li>Understanding file-based routing</li>
          <li>Creating your first pages</li>
        </ul>

        <h2>Prerequisites</h2>
        <p>Before starting, you should know:</p>
        <ul>
          <li>Basic HTML, CSS, and JavaScript</li>
          <li>React fundamentals</li>
        </ul>
      </article>
    </div>
  )
}

Dynamic Routes

Dynamic routes allow you to create pages with variable parameters using square brackets [].

Single Dynamic Route

Create pages/blog/[slug].js:

import { useRouter } from 'next/router'
import Head from 'next/head'
import Link from 'next/link'

export default function BlogPost() {
  const router = useRouter()
  const { slug } = router.query

  // In a real app, you'd fetch data based on the slug
  const post = {
    title: `Blog Post: ${slug}`,
    content: `This is the content for the blog post with slug: ${slug}`,
    date: '2024-01-15',
    author: 'John Doe'
  }

  return (
    <div>
      <Head>
        <title>{post.title}</title>
        <meta name="description" content={`Read about ${slug}`} />
      </Head>

      <nav>
        <Link href="/blog">← Back to Blog</Link>
      </nav>

      <article>
        <h1>{post.title}</h1>
        <div className="meta">
          <time>{post.date}</time>
          <span>By {post.author}</span>
        </div>
        <p>{post.content}</p>
      </article>
    </div>
  )
}

Usage Examples:

  • /blog/my-first-postslug = "my-first-post"
  • /blog/nextjs-tutorialslug = "nextjs-tutorial"
  • /blog/react-hooksslug = "react-hooks"

Multiple Dynamic Parameters

Create pages/blog/[year]/[month]/[slug].js:

import { useRouter } from 'next/router'
import Head from 'next/head'

export default function BlogPostWithDate() {
  const router = useRouter()
  const { year, month, slug } = router.query

  return (
    <div>
      <Head>
        <title>Blog Post from {month}/{year}</title>
      </Head>

      <main>
        <h1>Blog Post: {slug}</h1>
        <p>Published in {month}/{year}</p>
        <p>URL parameters:</p>
        <ul>
          <li>Year: {year}</li>
          <li>Month: {month}</li>
          <li>Slug: {slug}</li>
        </ul>
      </main>
    </div>
  )
}

URL Examples:

  • /blog/2024/01/my-postyear=2024, month=01, slug=my-post
  • /blog/2023/12/year-reviewyear=2023, month=12, slug=year-review

Catch-All Routes

Use [...param].js to catch multiple path segments:

Create pages/docs/[...slug].js:

import { useRouter } from 'next/router'
import Head from 'next/head'

export default function Docs() {
  const router = useRouter()
  const { slug } = router.query

  // slug will be an array of path segments
  const pathSegments = slug || []
  const currentPath = pathSegments.join('/')

  return (
    <div>
      <Head>
        <title>Documentation - {currentPath}</title>
      </Head>

      <main>
        <h1>Documentation</h1>
        <p>Current path: /{currentPath}</p>
        <p>Path segments:</p>
        <ul>
          {pathSegments.map((segment, index) => (
            <li key={index}>{segment}</li>
          ))}
        </ul>
      </main>
    </div>
  )
}

URL Examples:

  • /docs/getting-startedslug = ["getting-started"]
  • /docs/api/authentication/oauthslug = ["api", "authentication", "oauth"]
  • /docs/guides/deployment/vercelslug = ["guides", "deployment", "vercel"]

Navigation with Link Component

The Link component provides client-side navigation:

Basic Link Usage

import Link from 'next/link'

export default function Navigation() {
  return (
    <nav>
      {/* Basic link */}
      <Link href="/about">About</Link>

      {/* Link with custom styling */}
      <Link href="/contact" className="nav-link">
        Contact Us
      </Link>

      {/* Link with child component */}
      <Link href="/services">
        <button className="btn">Our Services</button>
      </Link>

      {/* External link (opens in new tab) */}
      <Link href="https://nextjs.org" target="_blank" rel="noopener">
        Next.js Docs
      </Link>
    </nav>
  )
}

Dynamic Links

import Link from 'next/link'

export default function BlogList({ posts }) {
  return (
    <div>
      {posts.map(post => (
        <article key={post.id}>
          <h2>
            <Link href={`/blog/${post.slug}`}>
              {post.title}
            </Link>
          </h2>
          
          {/* Link with query parameters */}
          <Link href={{
            pathname: '/blog/[slug]',
            query: { slug: post.slug, ref: 'homepage' }
          }}>
            Read More
          </Link>
        </article>
      ))}
    </div>
  )
}

Programmatic Navigation

Use the useRouter hook for programmatic navigation:

import { useRouter } from 'next/router'
import { useState } from 'react'

export default function SearchForm() {
  const router = useRouter()
  const [query, setQuery] = useState('')

  const handleSubmit = (e) => {
    e.preventDefault()
    
    // Navigate to search results
    router.push(`/search?q=${encodeURIComponent(query)}`)
    
    // Or with object syntax
    router.push({
      pathname: '/search',
      query: { q: query }
    })
  }

  const goBack = () => {
    router.back() // Go to previous page
  }

  const goHome = () => {
    router.push('/') // Navigate to homepage
  }

  return (
    <div>
      <form onSubmit={handleSubmit}>
        <input
          type="text"
          value={query}
          onChange={(e) => setQuery(e.target.value)}
          placeholder="Search..."
        />
        <button type="submit">Search</button>
      </form>
      
      <button onClick={goBack}>Go Back</button>
      <button onClick={goHome}>Go Home</button>
    </div>
  )
}

Route Groups and Layouts

Creating Shared Layouts

Create components/Layout.js:

import Head from 'next/head'
import Link from 'next/link'
import { useRouter } from 'next/router'

export default function Layout({ children, title = 'My Next.js App' }) {
  const router = useRouter()

  const isActive = (path) => {
    return router.pathname === path ? 'active' : ''
  }

  return (
    <div className="layout">
      <Head>
        <title>{title}</title>
        <meta name="viewport" content="width=device-width, initial-scale=1" />
      </Head>

      <header className="header">
        <nav className="nav">
          <Link href="/" className={isActive('/')}>
            Home
          </Link>
          <Link href="/about" className={isActive('/about')}>
            About
          </Link>
          <Link href="/blog" className={isActive('/blog')}>
            Blog
          </Link>
          <Link href="/contact" className={isActive('/contact')}>
            Contact
          </Link>
        </nav>
      </header>

      <main className="main">
        {children}
      </main>

      <footer className="footer">
        <p>&copy; 2024 My Next.js App. All rights reserved.</p>
      </footer>
    </div>
  )
}

Use Layout in pages/_app.js:

import Layout from '../components/Layout'
import '../styles/globals.css'

export default function App({ Component, pageProps }) {
  return (
    <Layout>
      <Component {...pageProps} />
    </Layout>
  )
}

Page-Specific Layouts

// pages/dashboard.js
import DashboardLayout from '../components/DashboardLayout'

export default function Dashboard() {
  return (
    <div>
      <h1>Dashboard</h1>
      <p>Welcome to your dashboard!</p>
    </div>
  )
}

// Add layout to specific page
Dashboard.getLayout = function getLayout(page) {
  return (
    <DashboardLayout>
      {page}
    </DashboardLayout>
  )
}

Advanced Routing Patterns

Optional Catch-All Routes

Use [[...slug]].js for optional catch-all routes:

// pages/shop/[[...slug]].js
import { useRouter } from 'next/router'

export default function Shop() {
  const router = useRouter()
  const { slug } = router.query

  if (!slug) {
    // /shop
    return <div>Shop Homepage</div>
  }

  if (slug.length === 1) {
    // /shop/electronics
    return <div>Category: {slug[0]}</div>
  }

  if (slug.length === 2) {
    // /shop/electronics/laptops
    return <div>Subcategory: {slug[1]} in {slug[0]}</div>
  }

  // /shop/electronics/laptops/macbook-pro
  return <div>Product: {slug.join('/')}</div>
}

Route Priority

Next.js follows this priority order:

  1. Static routes: /about.js
  2. Dynamic routes: /blog/[slug].js
  3. Catch-all routes: /docs/[...slug].js
pages/
├── blog/
│   ├── index.js           → /blog (highest priority)
│   ├── create.js          → /blog/create (higher priority)
│   ├── [slug].js          → /blog/:slug (lower priority)
│   └── [...slug].js       → /blog/* (lowest priority)

Handling Route Errors

Custom 404 Page

Create pages/404.js:

import Head from 'next/head'
import Link from 'next/link'

export default function Custom404() {
  return (
    <div className="error-page">
      <Head>
        <title>404 - Page Not Found</title>
      </Head>

      <main>
        <h1>404 - Page Not Found</h1>
        <p>Sorry, the page you are looking for does not exist.</p>
        
        <div className="actions">
          <Link href="/" className="btn">
            Go Home
          </Link>
          <Link href="/blog" className="btn">
            Visit Blog
          </Link>
        </div>
      </main>
    </div>
  )
}

Custom 500 Page

Create pages/500.js:

export default function Custom500() {
  return (
    <div>
      <h1>500 - Server Error</h1>
      <p>Something went wrong on our end.</p>
    </div>
  )
}

Best Practices for Routing

1. Consistent Naming Convention

# ✅ Good - kebab-case
pages/
├── about-us.js
├── contact-form.js
└── privacy-policy.js

# ❌ Avoid - mixed cases
pages/
├── aboutUs.js
├── ContactForm.js
└── privacy_policy.js

2. Logical Folder Structure

# ✅ Good - organized by feature
pages/
├── blog/
│   ├── index.js
│   ├── [slug].js
│   └── category/
│       └── [category].js
├── products/
│   ├── index.js
│   ├── [id].js
│   └── compare.js
└── user/
    ├── profile.js
    ├── settings.js
    └── dashboard.js

3. SEO-Friendly URLs

// ✅ Good - descriptive URLs
/blog/nextjs-complete-guide
/products/laptop-macbook-pro
/user/account-settings

// ❌ Avoid - unclear URLs
/blog/post1
/products/item123
/user/page2

Common Routing Mistakes

1. Incorrect File Placement

# ❌ Wrong - won't create routes
src/pages/about.js
components/pages/contact.js

# ✅ Correct
pages/about.js
pages/contact.js

2. Missing Default Export

// ❌ Wrong
export function About() {
  return <div>About</div>
}

// ✅ Correct
export default function About() {
  return <div>About</div>
}

3. Using Instead of Link

// ❌ Wrong - causes full page reload
<a href="/about">About</a>

// ✅ Correct - client-side navigation
<Link href="/about">About</Link>

What's Next?

Great job! You now understand:

  • ✅ File-based routing system
  • ✅ Static and dynamic routes
  • ✅ Nested routing with folders
  • ✅ Navigation with Link component
  • ✅ Programmatic navigation
  • ✅ Route groups and layouts
  • ✅ Advanced routing patterns

In Part 3, we'll explore:

  • Data fetching methods (getStaticProps, getServerSideProps)
  • Static Site Generation (SSG)
  • Server-Side Rendering (SSR)
  • Incremental Static Regeneration (ISR)
  • API routes and backend functionality

Practice Exercises

  1. Build a Portfolio Site

    • Create pages: Home, About, Projects, Contact
    • Add navigation between pages
    • Create a projects detail page with dynamic routing
  2. Create a Blog Structure

    • Build /blog with post listings
    • Create /blog/[slug] for individual posts
    • Add /blog/category/[category] for categorized posts
  3. Implement Search Functionality

    • Create a search form
    • Use programmatic navigation to redirect to results
    • Handle query parameters in the search results page
  4. Build a Documentation Site

    • Use catch-all routes for nested documentation
    • Create breadcrumb navigation
    • Implement a sidebar with active states

Ready for Part 3? Next, we'll dive into data fetching and learn how to build dynamic, data-driven applications with Next.js!

Continue following our comprehensive Next.js series to master every aspect of modern web development.

Share this article: