What is getStaticPaths() in Nextjs?

This article is originally posted at Link

In the last article, we learn how to use getStaticProps() in the Next.js application. In Next.js, we can pre-render a page during build time. We generate all the HTML code and data in advance. And the data is later cached by the server.

This approach works well for the static paths but, it fails when we have to pre-render pages for dynamic paths. And it makes sense. Let's say there is a blog and, there are multiple articles under it. And it next.js we defined dynamic paths like [blogId].js. As we already know, this path is valid for blog id 1, 2, 3, 4, and so on. There is no way Next.js knows how many pages it has to render.

To accomplish this, getStaticPath() is another function we use in this article.

To create the Next.js app, please follow this article.

Let's add a directory named blog under the pages folder. And add another route [blogId].js under the blog directory.

Just to start with, please add the following code:

function getBlogIdDetails(props){
    return (
        <div>
            Blog ID details pages
        </div>
    )
}

export  default getBlogIdDetails

Visit the URL, http://localhost:3000/blog/1 and, you will see "Blog ID details pages" on the browser.

Now create another file next.config.js and add the following:

module.exports = {
    webpack: (config, { buildId, dev, isServer, defaultLoaders, webpack }) => {
        config.node = {
            fs: 'empty', // This is required
        }
        return config
    }
}

Save the previous and create another file blogs_details.json and add the following content:

{

  "blogs": [

    { "id": "1", "title": "My First Blog", "description": "This is the content of first blog" },

    { "id": "2", "title": "My Second Blog", "description": "This is the content of second blog" },

    { "id": "3", "title": "My Third Blog", "description": "This is the content of third blog" },

    { "id": "4", "title": "My Fourth Blog", "description": "This is the content of fourth blog" },

    { "id": "5", "title": "My Fifth Blog", "description": "This is the content of fifth blog" },

    { "id": "6", "title": "My Sixth Blog", "description": "This is the content of sixth blog"}

  ]

}

The above file is our backend file, which we read using Next.js (Remember getStaticProps) and then dynamically load into our app.

Update the contents of the file [blogid].js

function getBlogIdDetails(props){
    return (
        <div>
            <h1><strong>{props.blogDetails.title} </strong> </h1>
            <br/>
            <p>{props.blogDetails.description}</p>
        </div>
    )
}

export async function getStaticProps() {
    return {
        props: {
            blogs: [{id: '1', title: 'title1'}, {id: '2', title: 'title2'}, {id: '3', title: 'title3'}]
        },
    };
}

export  default getBlogIdDetails

And now refresh the page "http://localhost:3100/blog/1".

At this point, you will see 'Error: getStaticPaths is required for dynamic SSG pages'

Remember we discussed how getStaticProps() fails in dynamic paths.

Let's get started with getStaticPaths().

Open the file [blogid.js] and import the following:

import fs from 'fs'
import path from 'path'

We need to import fs (to read the file) and path (To get the current working directory function) from node.js.
and then update the getStaticProps() function with:

export async function getStaticProps(context) {
    const { params } = context
    const blogId = params.blogid
    const fileToRead = path.join(process.cwd(), 'blogs_details.json')
    const data = JSON.parse(await fs.readFileSync(fileToRead))
    const blog = data.blogs.find(blog => blog.id === blogId)
    return {
        props: {
            blogDetails: blog
        },
    };
}

And add the function getStaticPaths() afterwards

export async function getStaticPaths() {
    return {
        paths: [
            {  params: { blogid: '1' } },
            {  params: { blogid: '2' } },
            {  params: { blogid: '3' } },
        ],
        fallback: false
    }
}

The getStaticPaths() function tells the Next.js to render the pages defined in paths. This function always returns the object.

Also, don't forget to add the fallback keyword to the getStaticPaths() function.

The complete file will look like

import fs from 'fs'
import path from 'path'
function getBlogIdDetails(props){
    return (
        <div>
            <h1><strong>{props.blogDetails.title} </strong> </h1>
            <br/>
            <p>{props.blogDetails.description}</p>
        </div>
    )
}
export async function getStaticProps(context) {
    const { params } = context
    const blogId = params.blogid
    const fileToRead = path.join(process.cwd(), 'blogs_details.json')
    const data = JSON.parse(await fs.readFileSync(fileToRead))
    const blog = data.blogs.find(blog => blog.id === blogId)
    return {
        props: {
            blogDetails: blog
        },
    };
}
export async function getStaticPaths() {
    return {
        paths: [
            {  params: { blogid: '1' } },
            {  params: { blogid: '2' } },
            {  params: { blogid: '3' } },
        ],
        fallback: false
    }
}
export  default getBlogIdDetails

The fallback can help us if we have too many blogs in our app, which is quite normal. A blog has 100s or 1000s of articles and it is not a good idea to pre-generate all those pages in advance. We don't want to pre-generate pages for the year-old blogs or the blogs which are not read enough by the users.

In that case, we set the fallback to true and delete all the other pages from the returned object. In our case let say, we don't want to pre-generate blog 2 and blog 3 pages.

export async function getStaticPaths() {
    return {
        paths: [
            {  params: { blogid: '1' } }
        ],
        fallback: true
    }
}

And also update the function getBlogIdDetails

function getBlogIdDetails(props){
    const { blogDetails } = props;
    // This will return Loading and prevents the page from being crashed    
    if (!blogDetails ) {
        return <p>Loading....</p>
    }

    return (
        <div>
            <h1><strong>{props.blogDetails.title} </strong> </h1>
            <br/>
            <p>{props.blogDetails.description}</p>
        </div>
    )
}

The above code will only pre-generate the pages for blog 1. Though all the paths to the blogs are still valid.

Thanks for reading up to here. Will see you in the next article.