Skip to content

Next.js 404 on Dynamic Routes Fix

DodaTech Updated 2026-06-24 3 min read

In this tutorial, you'll learn about Next.js 404 on Dynamic Routes Fix. We cover key concepts, practical examples, and best practices.

The Problem

Visiting a dynamic route like /posts/my-slug returns a 404 page even though the route is defined. Dynamic routes in Next.js require specific file naming and proper path generation.

Quick Fix

Step 1: Verify file naming convention

Dynamic route files must use square brackets:

pages/
├── posts/
│   └── [slug].jsx      // Right
│   └── slug.jsx        // Wrong — static route, not dynamic

Expected output: Next.js recognizes [slug].jsx as a dynamic route.

Step 2: Implement getStaticPaths for SSG

// Wrong — missing getStaticPaths
export default function Post({ post }) {
    return <article>{post.title}</article>;
}

// Right
export async function getStaticPaths() {
    const posts = await fetch('https://api.example.com/posts').then(r => r.json());

    return {
        paths: posts.map(post => ({
            params: { slug: post.slug },
        })),
        fallback: 'blocking',
    };
}

export async function getStaticProps({ params }) {
    const post = await fetch(`https://api.example.com/posts/${params.slug}`)
        .then(r => r.json());

    return { props: { post } };
}

Expected output: Each post is accessible at /posts/{slug}.

Step 3: Handle fallback modes correctly

// fallback: false — 404 for unknown paths
// fallback: true — show fallback UI, then render on client
// fallback: 'blocking' — SSR on first request, then cache

export async function getStaticPaths() {
    return {
        paths: [{ params: { slug: 'hello-world' } }],
        fallback: 'blocking', // recommended for SEO
    };
}

Expected output: Unknown paths are handled based on the fallback strategy.

Step 4: Use getServerSideProps for SSR-only routes

export async function getServerSideProps({ params }) {
    const post = await fetch(`https://api.example.com/posts/${params.slug}`)
        .then(r => {
            if (!r.ok) return { notFound: true };
            return r.json();
        });

    return { props: { post } };
}

Expected output: Dynamic routes render on the server and never return 404.

Step 5: Check for catch-all routes

// pages/posts/[...slug].jsx — catches /posts/a, /posts/a/b, etc.
export async function getStaticPaths() {
    return {
        paths: [],
        fallback: 'blocking',
    };
}

export async function getStaticProps({ params }) {
    const slug = params.slug.join('/');
    // handle multi-segment slug
}

Expected output: Multi-segment paths like /posts/2024/jan/update are handled.

Step 6: Return notFound from getStaticProps

export async function getStaticProps({ params }) {
    try {
        const post = await fetch(`/api/posts/${params.slug}`);
        if (!post.ok) return { notFound: true };
        return { props: { post } };
    } catch {
        return { notFound: true };
    }
}

Expected output: Invalid slugs return a proper 404 page.

Prevention

  • Use square brackets for dynamic route files
  • Always implement getStaticPaths for SSG dynamic routes
  • Use fallback: 'blocking' for SEO with dynamic content
  • Return { notFound: true } from data fetching functions for invalid routes

Common Mistakes with 404 page

  1. Overlapping type class instances that cause GHC to reject the program with ambiguous dispatch errors
  2. Non-exhaustive pattern matches that compile with warnings then crash at runtime
  3. Misunderstanding that String is [Char] with poor performance for large text operations

These mistakes appear frequently in real-world NEXTJS code. DodaTech's contributors have identified these patterns through analysis of open-source projects and production systems.

Practice Exercise

Write a pure function that safely divides two integers using Maybe, then test it with edge cases like division by zero and negative numbers.

This exercise reinforces the concepts covered in this guide. Try implementing it before checking online solutions.

FAQ

### What is the difference between fallback: true and fallback: 'blocking'?

fallback: true immediately returns an empty page and fetches data on the client. fallback: 'blocking' waits for the server to generate the page before sending it. Use 'blocking' for better SEO and true for faster initial loads.

Why does my static page return 404 after build?

The page was not included in paths returned by getStaticPaths. With fallback: false, any path not in paths returns 404. Use fallback: 'blocking' to generate pages on demand.

Can I have both static and dynamic routes in the same directory?

Yes. pages/posts/index.jsx is the listing page. pages/posts/[slug].jsx is the dynamic route. They coexist without conflicts. The index page takes priority for the exact /posts path.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro