Understanding next.js routing
Tags: nextjs, javascript, nextjsdynamicroute, webdev
Published: 2021-09-09 08:13:52

> This article is a quick introduction to next.js routing where I explain the steps I took to understand it.

When you first create a NEXT.js app with 

```
npx create-next-app
```

The file structure looks like this:

![file structure 1](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mlvf0fqvesbwl3ki471s.png)

the `pages` folder contains the page which will be rendered. 

Let's add a simple page `contact.js`

![file structure 2](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/e7385jbf53p74on3ybyo.png)

**contact.js**
```jsx
export default function Contact() {
    return (
        <div>
            <h1>Contact page</h1>
        </div>
    )
}
```

open `http://localhost:3000/contact`
and you should see your page title

![contact page](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cpa7554f1r1bql00zxke.png)


> Creating a large number of pages manually becomes quickly cumbersome. Fortunately NEXT.js provides dynamic routes. 

## Dynamic Routes

A dynamic route is basically a javascript file under `pages/` folder with special characters in its name. 

**basis examples**
- [id].js
- [slug].js
- [user_id].js
- [name].js

**catch all examples**
- [...id].js
- [...slug].js
- [...user_id].js
- [...name].js

Let's create a dynamic route.

![dynamic-route-part-1](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/c759ak6lcsxahdp4gp18.png)

**[slug].js**
```jsx
export default function DynamicPage() {
  return (
    <div>
      <p>dynamic page</p>
    </div>
  );
}

export async function getStaticPaths() {
  const posts = ["post/a", "post/b"];
  const paths = posts.map((post) => ({
    params: { slug: post },
  }));

  // { fallback: false } means other routes should 404.
  return { paths, fallback: false };
}
```

When you navigate to this page you get a `404` error page.


![post-a-not-found](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pux0hlw4h507xutzjclw.png)

> Seems legit since the folder structure `pages/[slug].js` tells next.js that the path to these pages is at the root of the site. Something like `/my-page` should work. right ? 

Let's navigate to `/a`. And then you get this error.

```
Error: getStaticPaths was added without a getStaticProps in /[slug]. Without getStaticProps, getStaticPaths does nothing
```
![error 1](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kb67kq27cytubypp3g97.png)

[reproduction repo](https://github.com/igorovic/understanding-nextjs-routing/tree/dynamic-page-part-1)

Let's add a `getStaticProps`

**[slug].js**
```jsx
export default function DynamicPage(props) {
  return (
    <div>
      <p>dynamic page</p>
      <p>{props.message}</p>
    </div>
  );
}

export async function getStaticProps(context) {
  return {
    props: { message: "dynamic page part 2" }, // will be passed to the page component as props
  };
}

export async function getStaticPaths() {
  const posts = ["post/a", "post/b"];
  const paths = posts.map((post) => ({
    params: { slug: post },
  }));

  return { paths, fallback: false };
}
```

The error is gone but the page is still not found. 

![/a not found](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/w6kumw5chzg17qyrkjdw.png)

[reproduction repo](https://github.com/igorovic/understanding-nextjs-routing/tree/dynamic-route-part-2)

### Fix: [slug].js

You need to add the page `a` in `getStaticPaths`

**[slug].js**

```jsx
export async function getStaticPaths() {
  const posts = ["post/a", "post/b", "a"];
  const paths = posts.map((post) => ({
    params: { slug: post },
  }));

  return { paths, fallback: false };
}
```
![a to getStaticPaths](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5g2upvug90zvho18md4l.png)

And it works

![/a works](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rqvgk96rz9lgg2uubpq6.png)

> But `/post/a` is still not found.

### Fix: /post/a

Remember that next.js routing is based on the folder and files structure. For `/post/a` to work we need to create one of these files:
- pages/post/a.js
- pages/post/[slug].js
- pages/post/[id].js

_The examples use `slug` and `id` but you can actually use any name since next.js use file structure to map the url path._


![file structure 3](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/c2k7l6hv5xksu4u7jere.png)

This file structure tells next.js to render pages if an http client requests one of the following path:
- /a
- /post/a
- /post/b
- /post/c
- /post/d
- ...

> Any path starting with `/post/` becomes a valid path. 

- `/post/a` path will be rendered using `pages/post/a.js`
- `/post/<any>` path will be rendered using `pages/post/[slug].js`

At this point, in `pages/[slug].js` paths `post/a`, `post/b` become irrelevant. We can delete them.

![delete irrelevant](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ac22qj463h3beei98ep8.png)

[reproduction repo](https://github.com/igorovic/understanding-nextjs-routing/tree/dynamic-route-part-3)


## Let's assume you have only two posts `a` and `b`

You don't want to render a page for non existing posts. To achieve this, you need to add `getStaticPaths` AND `getStaticProps` to file `pages/post/[slug].js`

**pages/post/[slug].js**
```js
export default function DynamicPage(props) {
  return (
    <div>
      <p>dynamic page </p>
    </div>
  );
}
export async function getStaticProps(context) {
  return {
    props: { message: "some post" }, 
  };
}

export async function getStaticPaths() {
  const posts = ["a", "b"]; // <= the magic happens here
  const paths = posts.map((post) => ({
    params: { slug: post },
  }));

  return { paths, fallback: false };
}
```

![limit paths](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dvc83uuaxsx6ycxkqio1.png)

This limits the valid url paths to:
- `/post/a`
- `/post/b`

[reproduction repo](https://github.com/igorovic/understanding-nextjs-routing/tree/dynamic-route-part-4)


### IMPORTANT

When you use `getStaticPaths` the `paths params` must match the filename. 

This will throw an error.

![match filename with path param](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6g0z6cncbmfvdqtce8ex.png)

**Error**
```
Error: A required parameter (id) was not provided as a string in getStaticPaths for /post/[id]
```
![error 2](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tcj5g17yyyb5hal90mfp.png)

#### documentation

[official doc](https://nextjs.org/docs/routing/dynamic-routes)