How to Generate Dynamic Routes with NextJS and Sanity.
Tags: nextjs, react, tutorial, beginners
Published: 2022-07-22 02:12:00

_Like in my last lesson, there will be a reference image of my file structure and file content so you can compare at the end._

## Adding links to each post

Inside of your `/pages/index.js` file, we need to add links to each post that will take us to that specific post's page.

Start by adding NextJS's Link feature.

```javascript
import Link from "next/link";
```
Now inside of our `postsElements` variable, wrap the entire element inside of a Link tag.

```javascript
const postsElements = posts.map((post, index) => (
    <Link key={index} href={`/posts/[slug]`} as={`/posts/${post.slug}`}>
      <div>
        <p>{new Date(post.publishedAt).toDateString().slice(4)}</p>
        <h3>{post.title}</h3>
      </div>
    </Link>
  ));
```

A few things are happening here.

1. We moved our `key={index}` from our div to our `Link` because it is now our outer most element.

2. There is an `href` attribute which you should be familiar with. This tells the browser where to go to when the element is clicked. I have it taking us to a `/posts/[slug]` file that we will create soon.

3. There is also an `as` attribute. This is a cosmetic attribute that turns our url into one that displays the `slug` value we added to each post. This will be used later to search for specific posts.

That's all the code needed to allow us to link to a post's page when clicked. Now it's time to add that page in our project.

<hr />

## Displaying a single post

It is time for us to create the file I just mentioned. 
Add a folder named `posts` in the pages folder, then add a file named `[slug].js` to that new folder. Your `pages` folder should now look like this.

![Pages folder](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ain194hprbxvfxpz9inv.png)

The reason we add the brackets around our file name is so that we can reference it later.

This is called a Dynamic Route and will be referenced to multiple times in this lesson so I advise you to check out the docs for it. [NextJS Dynamic Routes](https://nextjs.org/docs/routing/dynamic-routes)

Inside of our `/pages/posts/[slug].js/` file we just created, we are going to add our parts one at a time.

### Post component

```javascript
export default function Post() {
  return;
}
```
Standard React component. We will add the content of this component in a little bit.

### getStaticProps

```javascript
export async function getStaticProps({ params }) {
  const query = `*[_type == "post" && slug.current == $slug] {
    _id,
    title,
    publishedAt,
    'slug': slug.current,
    body
  }`;

  const options = { slug: params.slug };

  const post = await client.fetch(query, options);

  return {
    props: { post: post[0] },
  };
}
```

This is very similar to the getStaticProps we added in our `/pages/index.js` file in the last lesson, but with a few additions.

- We add our `{ params }` as an argument. 

 - To explain this a little, `getStaticProps` has access to an argument called `context`. Within this `context` argument you have a few pieces of information to pull from. We use `destructuring` to pull out the `params` value.
 - Inside of this `params` object is the url associated with this file. This is why we added the brackets to `[slug].js`. 


```javascript
[_type == "post" && slug.current == $slug]
```

- Same query as before, except this time we remove our order option since we are only fetching one post, and instead we add `&& slug.current == $slug`
 - This will fetch an entry that has a type of `post` AND has a `slug.current` (the url we provided in our post) that matches the current pages url (more on this in a bit).

```javascript
const options = { slug: params.slug }
```

- We assign the current url to an object.
 - Above I mentioned how we are using `params` to get our current url and naming it our `slug`. This is where that comes into play.
 - The unique url for this post is inside of `params.slug`. We assign that value to the key `slug` within an `options` object variable.

```javascript
const post = await client.fetch(query, options)
```

- Here we simply call our fetch like before, but now we add our `options` object we created.
 - This all works together because our `options` object has our url stored inside of it. 
 - Our query matches our post's `slug.current` with this url we saved in options to fetch us the post for the page we are on.

```javascript
return {
    props: { post: post[0] },
  };
```
- Finally we return our `post`. But since this fetched us an array, even though we only received 1 post, we make it easier on ourselves in our props by assigning the first (and only) object in our array to `post` so we can import it.

### getStaticPaths

By far the hardest section to explain. Honestly I don't think I can give this function justice in text, so I'm going to give the basics and provide links that will explain what's happening in greater detail.

I mentioned Dynamic Routes earlier when we created `[slug].js`. From the [NextJS getStaticProps Documentation](https://nextjs.org/docs/basic-features/data-fetching/get-static-paths) it explains that...

> _If a page has Dynamic Routes and uses getStaticProps, it needs to define a list of paths to be statically generated._

Well, we have Dynamic Routes, and we used getStaticProps, so we must need this. The idea of this is NextJS will now pre-render every path that you specify inside of getStaticPaths.

```javascript
export async function getStaticPaths() {
  const query = `*[_type == "post"]{ 'slug': slug.current }`;

  const posts = await client.fetch(query);

  const paths =
    posts?.map((post) => ({
      params: {
        slug: post.slug,
      },
    })) || [];

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

> To see examples similar to this one you can review [NextJS API-reference for getStaticPaths](https://nextjs.org/docs/api-reference/data-fetching/get-static-paths).

You might notice this seems very familiar to our `getStaticProps`. We need to create our query that fetches every post but only returns the `slug.current` value (the url we gave it), then give that query to our `client.fetch`.

But what is that `paths` variable? It looks more complicated than it is.

- First it checks if there are any posts in the first place, that's why we add the `?` to posts. If there isn't then we return an empty array with `|| []`. 
 - Essentially meaning `posts.map` if there are posts, if there aren't then return `[]`.

<br />

- Now inside of our `map`. For each post we create...
 - `{ params: { slug: post.slug } }`
 - This is fetching every single post's url and assigning it as a `param` for getStaticPaths to render at build time.

<br />

- Finally we return our `paths` and a `fallback: false`
 - Having fallback as false just means that other routes will 404 if you go to one that we didn't render in our map.

### Rendering the post

We are now going to use the data from the post we fetched inside of our `Post` component we created.

```javascript
export default function Post({ post }) {
  return (
    <div>
      <h1>{post.title}</h1>
      <span>{new Date(post.publishedAt).toDateString()}</span>
    </div>
  );
}
```
Similar code to what we used in our homepage to display each post, but this time we are including the day of the week in the date.

You might have noticed we aren't returning the `body` of our post, that is because this requires a little more effort to render (and to style) so I will be including that in the next short lesson.

<hr />

## Wrapping up

If you go to your homepage and click one of your posts it will take you to that post's url displaying the data of that specific post.

In the next lesson I will be showing you how to...
- Style a webpage with Styled-Components
- Style Rich Text components

We will also be including a `back` button to navigate back to our homepage from a post.



<hr />

## References

### File Structure


![Lesson 5 File Structure](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/w7u4euumjczs18plqqua.png)

### `pages/posts/[slug].js`

```javascript
import client from "../../lib/sanity";

export default function Post({ post }) {
  return (
    <div>
      <h1>{post.title}</h1>
      <span>{new Date(post.publishedAt).toDateString()}</span>
    </div>
  );
}

export async function getStaticProps({ params }) {
  const query = `*[_type == "post" && slug.current == $slug] {
    _id,
    title,
    publishedAt,
    'slug': slug.current,
    body
  }`;

  const options = { slug: params.slug };

  const post = await client.fetch(query, options);

  return {
    props: { post: post[0] },
  };
}

export async function getStaticPaths() {
  const query = `*[_type == "post"]{ 'slug': slug.current }`;

  const posts = await client.fetch(query);

  const paths =
    posts?.map((post) => ({
      params: {
        slug: post.slug,
      },
    })) || [];

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