Next.js 9.3 - statically generated pages

Next.js 9.3 - statically generated pages

Posted on by Petter Kjelkenes - last updated 26. March, 2020

Lets build a simple blog, index page will list all the blog posts in our cms. There will also be its own url for each blog post.

Why statically generated pages?

  • Hyperfast rendering on first render.
  • No server side calls to any database, which means saving resources.
  • Nothing scales better.
  • You can still have dynamic content, which we will look at in this blog post. But the web page is generated once.

1. Using Sanity as your Headless CMS

Go to https://www.sanity.io/ and create a free account. Sanity is a great headless CMS platform!

Lets get started.

Install the sanity client.

npm install -g @sanity/cli

Login to sanity in your cli.

sanity login

Create our new blog cms:

mkdir blog-cms
cd blog-cms
sanity init


Lets create a new post type.

Add schemas/post.js


export default {
  name: 'post',
  title: 'Post',
  type: 'document',
  fields: [
    {
      name: 'title',
      title: 'Title',
      type: 'string'
    },
    {
      name: 'slug',
      title: 'Slug',
      type: 'slug',
      options: {
        source: 'title',
        maxLength: 96
      }
    },
    {
      name: 'intro',
      title: 'Intro',
      type: 'string'
    },
    {
      name: 'body',
      title: 'Body',
      type: 'string'
    }
  ]
}

And lets add this to export, so sanity knows about it.

Add post to schemas/schema.js.

import createSchema from 'part:@sanity/base/schema-creator'
import schemaTypes from 'all:part:@sanity/base/schema-type'
import page from './page'

export default createSchema({
  name: 'default',
  types: schemaTypes.concat([
    post
  ])
})

Lets start up sanity, to boot our cms.

sanity start

2. Bootstrapping the frontend

mkdir blog
cd blog
npm init
npm install --save next react react-dom @sanity/cli
mkdir -p src/pages

Add start commands to package.json

{
  ...
  "scripts": {
    "dev": "next",
    "build": "next build",
    "start": "next start"
  }
  ...
}

This just lets you run e.g. npm run dev to start the development server.

Index page - list our posts

Lets add our index page, which will list out all our blog posts.

Create the file src/pages/index.tsx

import React from 'react'
import Link from 'next/link'
import sanityClient from '@sanity/client'

const query = `*[_type == "post"]  | order(publishedAt desc) {
  _id,
  title,
  slug,
  intro
}[0...50]
`

const IndexPage = ({ posts }) => {
  return (
    <div>
      {posts.map(post => (
        <div key={post._id}>
          <h2>{post.title}</h2>
          <Link href="/[slug]" as={`/{post.slug.current}`}>
            <a>
              Read more about {post.title}...
            </a>
          </link>
        </div>
      ))}
    </div>
  )
}

export const getStaticProps = async () => {
  const sanity = sanityClient({
  // Find your project ID and dataset in `sanity.json` in your studio project
  projectId: 'sanity-project-id',
  dataset: 'production',
  useCdn: true,
})
  const posts = await sanity.fetch(query)
  return {
    props: {
      posts
    },
  }
}

export default IndexPage

So this is a very simple blog listing page.

The important part here is that we are exporting getStaticProps. Also the index page does not have any query parameters, so we dont need any context to render all our posts.

Lets try it.

npm run build

Npm run build will statically build this page, and take care of running a query to sanity api, and then build a static page.

After the build, you can run

npm run start

Congrats! You now have your first statically generated page, with dynamic content!

The blog post page

Now for the next page we are going to build, we need to use another export that nextjs has availble getStaticPaths.

This is because we are going to dynamically respond to existing blog posts from the URL parameters.

e.g. url http://localhost:3000/my-blog-post should should find the correct blogpost in sanity and display it.

For this we need to use getStaticPaths.

import React from 'react'
import Link from 'next/link'
import sanityClient from '@sanity/client'

const query = `*[_type == "post" && slug.current == $slug] {
  _id,
  title,
  intro
}[0]
`
const queryAll = `*[_type == "post" && slug.current != ''] {
  'slug': slug.current
}
`
`

const getSanityClient = () => {
  return sanityClient({
    // Find your project ID and dataset in `sanity.json` in your studio project
    projectId: 'sanity-project-id',
    dataset: 'production',
    useCdn: true,
  })
}

const PostPage = ({ post }) => {
  return (
    <div>
      <h2>{post.title}</h2>
      <p>{post.intro}</p>
      ...
    </div>
  )
}

export const getStaticProps = async context => {
  const post = await sanity.fetch(query, { slug: context.params.slug })
  return {
    props: {
      post
    },
  }
}


export const getStaticPaths = async () => {
  const pages = await sanity.fetch(queryAll) || []
  const paths = pages.map(page => ({
    params: { slug: page.slug },
  }))
  return { paths, fallback: false }
}

export default PostPage

So to build our post page:

  • First find all our blog posts that are published ( getStaticPaths() ). Get the slugs, so we can send this in paths to the real Post page. Here we QUERY sanity, and ask for all the posts that has a slug defined (and is published).
  • For each slug, we query sanity per post and fetch all content based on that slug.
  • Behind the scenes Nextjs. will build static pages for all your blog posts. so if you have two posts with slug hello-world and my-first-post. Two pages will be generated and http://localhost:3000/hello-world and http://localhost:3000/my-first-postw will be accessable as a static generated page.

Test it all

npm run build
npm run start

Congrats! You have a fully statically generated site.

You will notice that NO api request to sanity is made if you go into e.g. http://localhost.3000/my-first-post

Whats next?

Currently, you will have to build the codebase everytime you edit something in sanity! Next up we will look at how we can build our codebase once we edit something in sanity. That can be done with webhooks, and a service such as now.sh.

In the next episodes we will look at how we can

Comments