Nextjs Dynamic Routes and URL Parameters Step by step Implementation and Top 10 Questions and Answers
 .NET School AI Teacher - SELECT ANY TEXT TO EXPLANATION.    Last Update: April 01, 2025      21 mins read      Difficulty-Level: beginner

Next.js Dynamic Routes and URL Parameters

Introduction

Next.js, a popular JavaScript framework for building server-rendered and statically generated web applications, offers a robust way to handle routing and URL parameters. One of the standout features of Next.js is its support for dynamic routes, which allow developers to create dynamic URLs based on the parameters provided in the URL path. This article will provide a detailed explanation of Next.js dynamic routes and URL parameters, along with important information and examples.

Understanding Dynamic Routes

Dynamic routes in Next.js are used to create routes that depend on the URL path. This is particularly useful for applications that need to display different content based on unique identifiers, like a blog post's ID or a user's username. In Next.js, dynamic routes are created by adding square brackets [] inside the page names in the pages directory. For example, [id], [slug], or any other name.

Creating a Dynamic Route

To create a dynamic route, you simply need to add a page with a filename wrapped in square brackets inside the pages directory. Here is a step-by-step process to create a dynamic user profile route:

  1. Create the Page: Create a new file named [id].js inside the pages directory.

    pages/
    └── user/
        └── [id].js
    
  2. Access URL Parameters: Inside the [id].js file, you can access the dynamic route parameters via the props object passed to the page component.

    export async function getServerSideProps({ params }) {
      // Fetch data based on params.id
      const res = await fetch(`https://api.example.com/users/${params.id}`);
      const data = await res.json();
    
      // Pass data to the page via props
      return { props: { user: data } };
    }
    
    function UserProfile({ user }) {
      return (
        <div>
          <h1>User Profile</h1>
          <p>Name: {user.name}</p>
          <p>Email: {user.email}</p>
        </div>
      );
    }
    
    export default UserProfile;
    

    In this example, the getServerSideProps function is used to fetch data for a user based on the id parameter from the URL.

Example: Dynamic Blog Post Page

For a blog application, you might have a route like /posts/[slug], where slug is the unique identifier for each blog post. Here's an example of how you can set this up:

  1. Create the Page: Create a new file named [slug].js inside the pages/posts directory.

    pages/
    └── posts/
        └── [slug].js
    
  2. Access URL Parameters: Inside the [slug].js file, access the dynamic route parameter and fetch the blog post data.

    export async function getServerSideProps({ params }) {
      // Fetch post data based on params.slug
      const res = await fetch(`https://api.example.com/posts/${params.slug}`);
      const post = await res.json();
    
      // Pass data to the page via props
      return { props: { post } };
    }
    
    function Post({ post }) {
      return (
        <div>
          <h1>{post.title}</h1>
          <p>{post.body}</p>
        </div>
      );
    }
    
    export default Post;
    
  3. Routing to the Dynamic Route: You can navigate to the dynamic route using the Link component or programmatically with Router.

    import Link from 'next/link';
    
    function BlogPosts({ posts }) {
      return (
        <div>
          {posts.map((post) => (
            <div key={post.slug}>
              <Link href={`/posts/${post.slug}`}>
                <a>{post.title}</a>
              </Link>
            </div>
          ))}
        </div>
      );
    }
    
    export async function getStaticProps() {
      const res = await fetch('https://api.example.com/posts');
      const posts = await res.json();
    
      return { props: { posts } };
    }
    
    export default BlogPosts;
    

Nested Dynamic Routes

Next.js also supports nested dynamic routes, enabling deeper URL structures relevant to certain applications. For example, you can have a route like /users/[userId]/posts/[postId].

  1. Create the Page: Create a new file named [postId].js inside the pages/users/[userId]/posts directory.

    pages/
    └── users/
        └── [userId]/
            └── posts/
                └── [postId].js
    
  2. Access URL Parameters: Inside the [postId].js file, access both userId and postId parameters.

    export async function getServerSideProps({ params }) {
      const res = await fetch(`https://api.example.com/users/${params.userId}/posts/${params.postId}`);
      const post = await res.json();
    
      return { props: { post } };
    }
    
    function UserPost({ post }) {
      return (
        <div>
          <h1>{post.title}</h1>
          <p>{post.body}</p>
        </div>
      );
    }
    
    export default UserPost;
    

Handling Optional URL Parameters

Sometimes, URL parameters might be optional, meaning they do not always need to be present. In such cases, Next.js provides a way to handle optional parameters using the [[...slug]] syntax. This is useful for creating flexible routing scenarios.

  1. Create the Page: Create a new file named [[...slug]].js inside the pages directory.

    pages/
    └── [[...slug]].js
    
  2. Access URL Parameters: Inside the [[...slug]].js file, access the slug parameter, which will be an array.

    export async function getServerSideProps({ params }) {
      if (params.slug) {
        // Handle the case where slug is present
        console.log(params.slug); // e.g. ['posts', 'slug']
      } else {
        // Handle the case where slug is not present
      }
    
      return { props: { } };
    }
    
    function FlexibleRoute({ }) {
      return <div>Flexible Route</div>;
    }
    
    export default FlexibleRoute;
    

Important Information

  • Static vs. Server-Side Generation: Dynamic routes can be generated at build time (static generation) or at request time (server-side generation). Use getStaticPaths for static generation with dynamic parameters.

    export async function getStaticPaths() {
      const res = await fetch('https://api.example.com/users');
      const users = await res.json();
    
      const paths = users.map((user) => ({
        params: { id: user.id.toString() },
      }));
    
      return { paths, fallback: false };
    }
    
    export async function getStaticProps({ params }) {
      const res = await fetch(`https://api.example.com/users/${params.id}`);
      const user = await res.json();
    
      return { props: { user } };
    }
    
  • Fallback Pages: When using getStaticPaths, you can specify a fallback option. fallback: true will enable the page to be rendered when a new path is visited, with a loading state shown initially. fallback: 'blocking' will block rendering until the data is fetched and the page is ready.

  • Route Masking: Next.js allows you to create clean and readable URLs by masking the actual dynamic routes. For example, you can map /product/123 to /product/[id] without exposing the dynamic structure.

  • Type Safety: With TypeScript, you can add type definitions for your URL parameters to ensure type safety and improve developer experience.

    import { GetServerSideProps } from 'next';
    
    interface PageProps {
      user: {
        id: string;
        name: string;
        email: string;
      };
    }
    
    const UserProfile: React.FC<PageProps> = ({ user }) => {
      return (
        <div>
          <h1>User Profile</h1>
          <p>Name: {user.name}</p>
          <p>Email: {user.email}</p>
        </div>
      );
    };
    
    export const getServerSideProps: GetServerSideProps<PageProps> = async ({ params }) => {
      const res = await fetch(`https://api.example.com/users/${params.id}`);
      const data: { id: string; name: string; email: string } = await res.json();
    
      return { props: { user: data } };
    };
    
    export default UserProfile;
    

Conclusion

Dynamic routes and URL parameters are a powerful feature in Next.js that enable developers to build flexible and dynamic web applications. By leveraging the bracket notation in the pages directory, you can easily create routes that respond to URL parameters and fetch relevant data. This not only enhances the application's content delivery but also improves the overall user experience by providing clean and intuitive navigation structures. Understanding and effectively using dynamic routes is essential for any Next.js developer aiming to build sophisticated web applications.




Certainly! Here’s a comprehensive step-by-step guide for beginners to understand how to work with Next.js Dynamic Routes and URL Parameters:

Understanding Next.js Dynamic Routes and URL Parameters

Next.js provides a powerful feature called Dynamic Routes that allows you to create dynamic pages based on URL parameters. This is particularly useful for applications that require rendering content dynamically, such as blog posts, product listings, or user profiles. In this guide, we'll walk through an example of how to set up a dynamic route and pass URL parameters to it.

Step 1: Set Up Your Next.js Application

First, you need to set up a new Next.js application if you haven’t already. You can do this by using the create-next-app command-line tool:

npx create-next-app@latest my-next-app
cd my-next-app

Step 2: Create a Static Page for Context

Before diving into dynamic routes, let's create a simple static page. This will serve as the home page for our application.

Navigate to the pages directory and modify the index.js file like this:

// pages/index.js
export default function Home() {
  return (
    <div style={{ padding: '20px' }}>
      <h1>Welcome to My Blog!</h1>
      <p>Check out our latest posts:</p>
      <ul>
        <li><a href="/posts/1">Post 1: Introduction to Next.js</a></li>
        <li><a href="/posts/2">Post 2: Dynamic Routes in Next.js</a></li>
        <li><a href="/posts/3">Post 3: Optimizing Performance</a></li>
      </ul>
    </div>
  );
}

This page contains links to three different blog posts. When a user clicks one of these links, we want to render a dynamic page based on the post ID.

Step 3: Create a Dynamic Route

Dynamic routes in Next.js are created by adding a file with [param].js in the pages directory. The name of the file (in between the brackets) acts as the parameter.

Create a new directory named posts inside the pages folder and then create a file named [id].js within the posts directory:

mkdir pages/posts
touch pages/posts/[id].js

Now, let’s define the structure and logic for displaying a blog post based on the URL parameter.

Step 4: Fetch Data Based on URL Parameter

In this example, we'll simulate fetching data from an external API or a database. For simplicity, let's use hardcoded data.

Edit the [id].js file to look like this:

// pages/posts/[id].js
export default function Post({ post }) {
  return (
    <div style={{ padding: '20px' }}>
      <h1>{post.title}</h1>
      <p>{post.content}</p>
      <a href="/">Back to Home</a>
    </div>
  );
}

// This function will be called at build time
export async function getStaticPaths() {
  // Simulate fetching data from an API or database
  const posts = [
    { id: '1', title: 'Introduction to Next.js', content: 'Next.js is a powerful React framework...' },
    { id: '2', title: 'Dynamic Routes in Next.js', content: 'Dynamic routes allow you to create pages based on URL parameters...' },
    { id: '3', title: 'Optimizing Performance', content: 'Optimizing performance in Next.js involves...' },
  ];

  // Get the paths we want to pre-render based on posts
  const paths = posts.map((post) => ({
    params: { id: post.id.toString() },
  }));

  // We'll pre-render only these paths at build time.
  // { fallback: false } means other routes should 404.
  return { paths, fallback: false };
}

// This function will be called at build time
export async function getStaticProps({ params }) {
  // Simulate fetching data from an API or database based on the route parameter
  const posts = [
    { id: '1', title: 'Introduction to Next.js', content: 'Next.js is a powerful React framework...' },
    { id: '2', title: 'Dynamic Routes in Next.js', content: 'Dynamic routes allow you to create pages based on URL parameters...' },
    { id: '3', title: 'Optimizing Performance', content: 'Optimizing performance in Next.js involves...' },
  ];

  const post = posts.find((p) => p.id === params.id);

  // Pass post data to the page via props
  return { props: { post } };
}

Explanation of Code

  1. Default Function: Post is the default export which receives post data as a prop and renders it.

  2. getStaticPaths: This function gets called at build time and returns a list of possible routes. In this case, it returns an array of objects with the id of each post.

  3. getStaticProps: This function also runs at build time. It receives the parameters from the URL (using params). It uses this id to fetch the corresponding post data and then passes the post object as props to the Post function.

Step 5: Run the Application

Now you can run your Next.js application and test the dynamic routes. Start the development server:

npm run dev

Open your browser and navigate to http://localhost:3000. You should see the home page with links to three different blog posts.

Click on any of the links, and you’ll be directed to a dynamic page for that post based on the URL parameter. For example, clicking on "Post 1: Introduction to Next.js" will take you to http://localhost:3000/posts/1 and display the content for that specific post.

Step 6: Data Flow Overview

Let's quickly go over how the data flows in this application:

  1. Static Generation at Build Time:

    • getStaticPaths is called to determine all possible routes.
    • getStaticProps is then called for each route to fetch the necessary data.
  2. Rendering:

    • The fetched data is passed to the Post component as props.
    • The component renders the page based on the provided props.
  3. User Interaction:

    • When a user clicks a link, the application navigates to the corresponding dynamic route.
    • The pre-generated page (with the appropriate props) is served to the user.

Dynamic Routes with API Data

In a real-world application, you'd fetch data from an external source like an API. Here's how you might modify getStaticProps to fetch data from an API:

// pages/posts/[id].js
export async function getStaticProps({ params }) {
  // Fetch data from an external API
  const res = await fetch(`https://api.example.com/posts/${params.id}`);
  const post = await res.json();

  // Pass the data from the API to the Post component via props
  return { props: { post } };
}

Conclusion

In this guide, you learned how to create dynamic routes in Next.js and pass URL parameters to them. We covered the entire process, from setting up the application to running it and understanding the data flow. Dynamic routes are a key feature of Next.js that allow for powerful and flexible routing in your applications. Now, go ahead, implement dynamic routing in your own Next.js projects and see the magic happen!




Top 10 Questions and Answers on Next.js Dynamic Routes and URL Parameters

1. What are Dynamic Routes in Next.js?

Answer: Dynamic routes enable you to create pages with parameters. For example, a dynamic route could be /posts/[id], which would match /posts/1 or /posts/2. This means you don't need to define a separate page for every post ID; instead, Next.js will generate the necessary HTML and server-side code to handle all possible ID values.

2. How do I create a Dynamic Route in Next.js?

Answer: Creating a dynamic route is straightforward. You simply add a file with square brackets in your pages directory. For example, to create a dynamic route for blog posts, you would create a file named pages/posts/[id].js. Within this file, you can access the dynamic segment from the URL like so:

// pages/posts/[id].js
import { useRouter } from 'next/router'

const Post = () => {
  const router = useRouter();
  const { id } = router.query;
  
  return <h1>Post: {id}</h1>;
}

export default Post;

3. Can I have Nested Dynamic Routes in Next.js?

Answer: Yes, you can create nested dynamic routes. For instance, if you want to manage categories and subcategories, you can create a route like pages/categories/[category]/[subcategory].js. This allows you to handle URLs such as /categories/books/fantasy.

4. How can I access Query Parameters using Dynamic Routes?

Answer: In addition to URL parameters, you can also access query parameters using the useRouter hook or the getServerSideProps function. Here’s an example using useRouter:

// pages/products/[id].js
import { useRouter } from 'next/router'

const Product = () => {
  const router = useRouter();
  const { id } = router.query;
  const { search } = router.query; // Accessing a query parameter named "search"

  return <h1>Product: {id} - Search: {search}</h1>;
}

export default Product;

With getServerSideProps, you can access the query object in the context:

// pages/products/[id].js
export async function getServerSideProps(context) {
  const { id } = context.params;
  const { search } = context.query;

  // Fetch data based on id and search
  
  return {
    props: { 
      id,
      search,
    },
  };
}

const Product = ({ id, search }) => (
  <h1>Product: {id} - Search: {search}</h1>
)

export default Product;

5. Is there a way to handle optional URL segments in Dynamic Routes?

Answer: Next.js does not natively support optional URL segments directly in the file structure due to its design philosophy around explicitness and clarity. However, you can handle optional segments by providing fallbacks or default values within your components.

For example, consider a route [slug].js. If you want it to work both for /product-1 and / (root), you’d need to handle this logic within your component:

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

const Page = () => {
  const router = useRouter();
  const { slug } = router.query;

  // Handle the case where slug might be undefined
  const displaySlug = slug || "Home";

  return <h1>Page: {displaySlug}</h1>;
}

export default Page;

Alternatively, you can use higher-order components or routing utilities to manage more complex scenarios.

6. What is the benefit of using Dynamic Routes in Next.js?

Answer: Dynamic routes offer several benefits:

  • Scalability: They allow you to manage large numbers of similar pages without creating individual files.
  • Maintainability: You write less repetitive code.
  • Performance: Next.js optimizes these routes for static generation or server-side rendering, improving SEO and load times.

7. How do I perform Client-side Navigation with Dynamic Routes?

Answer: To navigate between dynamic routes on the client side, you can use the next/link component. Here's an example:

// pages/index.js
import Link from "next/link";

const Home = () => (
  <ul>
    {[1, 2, 3].map((id) => (
      <li key={id}>
        <Link href={`/posts/${id}`}>
          <a>Post {id}</a>
        </Link>
      </li>
    ))}
  </ul>
);

export default Home;

When users click on a link, Next.js will pre-fetch the page on the client side for faster navigation.

8. How can I handle Catch-alls in Next.js Dynamic Routes?

Answer: Catch-all routes help you capture multiple URL segments into a single dynamic route. These are useful when you cannot predict how many segments your URLs will contain.

Catch-all parameters are surrounded by three dots (...) in filenames. For example, to define a route that captures any number of segments after /posts, you would create pages/posts/[...slug].js:

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

const Post = () => {
  const router = useRouter();
  const { slug } = router.query;

  return (
    <>
      <h1>Catch-all Example</h1>
      <p>Slug: {slug.join(", ")}</p>
    </>
  );
};

export default Post;

This route would match paths like /posts/1/2/3 and /posts/react/nextjs, making slug an array of strings.

9. Can I use Static Generation (SSG) for Dynamic Routes in Next.js?

Answer: Absolutely! Dynamic routes can leverage static generation (SSG). With SSG, Next.js generates HTML files at build time and reuses them on each request.

To use SSG with dynamic routes, you must export an getStaticPaths function from the same file that exports your page component. This function tells Next.js which dynamic routes should be pre-generated at build time.

Here's an example:

// pages/posts/[id].js
import { getPostsById } from "@/lib/posts";

const Post = ({ post }) => <article>{post.title}</article>;

export async function getStaticProps({ params }) {
  const post = await getPostsById(params.id);
  return { props: { post } };
}

export async function getStaticPaths() {
  // Fetch existing posts from the filesystem (or API)
  const posts = [...];
  const paths = posts.map(post => ({
    params: { id: post.id.toString() },
  }));
  
  return { paths, fallback: false };
}

export default Post;

By exporting getStaticPaths, Next.js statically generates the specified paths during the build process.

10. How do I handle Fallback Pages for Dynamic Routes during SSG?

Answer: When using getStaticPaths with fallback: true or "blocking", you can handle pages that were not generated during the build phase but are still valid routes. With fallback: true, ungenerated pages are rendered on the server, then subsequently cached as static assets. With fallback: "blocking", Next.js waits for the page to be fully generated before serving it to the user, ensuring content is always up-to-date but can lead to slower initial load times.

Here’s an example with fallback: "blocking":

// pages/posts/[id].js
import { useState, useEffect } from "react";
import { getPostById } from "@/lib/posts";

const Post = ({ fallbackData = {} }) => {
  const [post, setPost] = useState(fallbackData || null);

  useEffect(() => {
    if (!post) {
      getPostById(router.query.id)
        .then(data => setPost(data));
    }
  }, [post]);

  return post ? (
    <article>{post.title}</article>
  ) : <p>Loading...</p>;
}

export async function getStaticProps({ params }) {
  const post = await getPostById(params.id);
  if (!post) {
    return { notFound: true }; // If the data doesn't exist
  }
  
  return { props: { fallbackData: post } };
}

export async function getStaticPaths() {
  // Fetch a subset of existing posts (e.g., most popular)
  const posts = [...];
  return {
    paths: posts.map(post => ({ params: { id: post.id.toString() } })),
    fallback: "blocking",
  };
}

export default Post;

In this setup, if a user accesses a post that wasn't generated at build time, Next.js will server-render the page before making it available to everyone, effectively handling fallback cases seamlessly while maintaining performance optimizations.


Dynamic routes and URL parameters are powerful features in Next.js that help you build scalable, maintainable applications with efficient routing mechanisms. By leveraging these tools, developers can create dynamic web experiences that cater to diverse user needs while optimizing for performance and SEO.