Next.js provides multiple methods of pre-rendering your content for your app. You can check out a comparison of these different methods here, but today I’ll be covering Static Site Generation with getStaticProps().

Static Site Generation pre-renders your content at build time, so that it can serve that part of your app as a static HTML site, rather than a dynamic site that relies more on JavaScript. It can help speed up your site, and is really useful for situations where the content won’t change often, like blog posts, or portfolios.

getStaticPaths

Pre-rendering in Next.js is done through generating props for your page. Then, Next.js renders a page for each version of the props you provide.

For some pages, there might only be one version, e.g. something like an intro page. There might be many versions of a page though, for example an image gallery with a page for each image, or a blog post with a page for each post. In getStaticPaths() you generate the path parameters for each version of the page you want to generate.

Let’s explore how to implement this in a Next.js app. I’ll be creating a small app to display some pokemon data, using the data from PokeAPI.

The way I’m going to implement my small pokedex is through a single page for each pokemon, getting the pokemon name with a dynamic path parameter. This means I need to provide Next.js with a list of pages to produce, which is what we do in getStaticPaths():

export async function getStaticPaths() {
    const pokemon = await axios
        .get<PokemonResponse>('https://pokeapi.co/api/v2/pokemon?limit=9')
        .then(({ data }) =>
            data.results.map((p) => `/examples/pokemon/${p.name}`)
        );

    return {
        paths: pokemon,
        fallback: false, //Means anything else will 404
    };
}

I’m getting my list of Pokémon with an Axios get call to the Pokémon API. You can open the api endpoint in your browser to see the result, but it’s essentially a list of the first 9 Pokémon in objects, which I’m then converting to a path.

The fallback option tells Next.js whether to 404 if we give it a path we haven’t told it to produce, or whether it should try and render the page dynamically. For our case you might want it to 404, since there’s only a certain number of Pokemon, or you might want it to navigate to the page either way, and handle the Pokemon not existing inside the component.

Inside my Pokémon array we have:

[
  '/examples/pokemon/bulbasaur',
  '/examples/pokemon/ivysaur',
  '/examples/pokemon/venusaur',
...
]

All the paths to produce. An important thing to note here is that these have to be the full paths, i.e. everything after the URL.

getStaticProps()

In the previous example, we’ve essentially produced the props/parameters to generate our component props. In getStaticProps() we can do something with the page parameters we’ve just produced, and these get passed to our page component.

export async function getStaticProps({ params }: GetStaticPropsContext) {
    console.log('Params:', params);
    if (!params) return { props: {} };
    const pokemon = await axios
        .get<SinglePokemonResponse>(
            'https://pokeapi.co/api/v2/pokemon/' + params['pokemon']
        )
        .then(({ data }) => {
            return {
                name: data.name,
                sprite: data.sprites.front_default,
                types: data.types,
            };
        });
    return { props: { pokemon } };
}

In this function we can extract the name of our Pokemon from the path parameters. When Next.js builds our pages, it will call this function with all the paths generated in the previous step.

With the name of our Pokémon, I can just perform another Axios call to the Pokémon api. This time we get back a lot of data about our Pokémon. The important parts I’m extracting for our site are just the name, a link to the sprite for the Pokémon, and the Pokémon’s types.

Our Component

The final step is the props we’ve just produced get passed on to our page:

export default function Pokemon({ pokemon }: { pokemon: Pokemon }) {
    const { name, sprite, types } = pokemon;
    return (
        <div className="flex h-screen w-full items-center justify-center bg-stone-900 text-white">
            <div className="flex flex-col items-center justify-center rounded bg-stone-800 p-10 shadow-2xl">
                <h1 className="text-6xl font-bold capitalize">{name}</h1>
                <Image
                    src={sprite}
                    alt={`${name} sprite`}
                    width={300}
                    height={300}
                />
                <div className="text-4xl capitalize">{`${types[0].type.name}${
                    types.length > 1 ? ' ' + types[1].type.name : ''
                }`}</div>
            </div>
        </div>
    );
}

This is a really simple component, I’m just displaying the data received from getStaticProps(), with some styling from Tailwind.

Here’s what our page looks like:

Thanks to getStaticProps(), we have no API calls inside the actual page, and all the content is pre-rendered. Your page will load faster, since it can send less JavaScript, and the bundle size is smaller. Another big benefit of this is that you can export your app as a static site, and host it easily with services like GitHub Pages for free.

And that’s all, thanks for reading! Static Site Generation with getStaticProps() is a great pre-rendering technique, perfectly suited for situations like this, where data won’t change often. If you liked this article, feel free to leave a comment below!

Avatar photo
👋 Hey, I'm Omari Thompson-Edwards
Hey, I'm Omari! I'm a full-stack developer from the UK. I'm currently looking for graduate and freelance software engineering roles, so if you liked this article, reach out on Twitter at @marile0n

💬 Leave a comment

Your email address will not be published. Required fields are marked *

We will never share your email with anyone else.