Static site generation with NextJs and a headless CMS
In this article, we will briefly explore the difference between server-side rendering(SSR) and static site generation(SSG) and implement both of them in a simple NextJS application fetching data from a Headless CMS.
What and why use SSR or SSG
Modern websites to always stay performant for users and search engines are employing SSR or SSG techniques. NextJS is a great React framework to implement both of them quickly and straightforwardly, and we will use it to achieve them. But first, explore the differences between them and some pros and cons.
SSR-enabled pages are generated runtime on the server at each user request(if not cached in someway!).Instead, SSG pages are created at build time and served to the users.
The main advantages of SSR pages are that the content is always up-to-date, and there is no need to trigger a rebuild and redeploy of the website when some content changes in the CMS. The downside is that each request executes server-side code to create the HTML by fetching content from the cms Delivery API; this, of course, creates a slower response for the users.
With SSG, all the pages are pre-generated at build-time. So they can easily be distributed through CDN, creating the fastest possible experience for the users and also making Google happy for SEO purposes. The main drawback is that each time content changes in the CMS, a new build it's needed to make content live, and this may be suboptimal if your website needs constant changes!
Setting up a example application with NextJS and Headless CMS support
To complete this mini-tutorial, you will need git and Nodejs ( v10.13 or above) installed and working in your machine and a trial account of ContentChef, the headless CMS that we are going to use in these examples.
Let's start by cloning the repo of our NextJS starter and installing all the dependencies.
git clone [email protected]:ContentChef/nextjs-starter.git
cd nextjs-starer
npm install
This is a brand-new NextJs application with the ContentChef SDK installed, as we will use to fetch content from the headless CMS API.
It's a simple website that displays a list of websites and a detail page for each of them.All the sample data are pre-loaded on ContentChef account so you don't have to do nothing about that.
Get your SpaceID and Online API key from the dashboard. You will find them on the homepage of the dashboard like in the screenshot below
Now let's open the file ./services/contentChefClient.js
and fill the pieces of information.
import ContentChefClient, { createUrl } from '@contentchef/contentchef-node';
class ContentChef {
client;
targetDate;
defaultChannel = 'example-ch';
onlineChannel;
constructor() {
this.client = ContentChefClient({
spaceId: 'your-contentChef-spaceId',
}, this.targetDate);
this.onlineChannel = this.client.onlineChannel('your-contentChef-api-key', this.defaultChannel);
}
}
Try the application to be sure that everything is in place by running:
npm run dev
Open the browser and head to http://localhost:3000, and you should see the list of the websites, and by clicking on one of them, you will access to the detail of that website.
Great, this simple app has already SSR enabled!
In fact, NextJS makes it extremely easy to create applications with SSR because you just need to export a function named getServerSideProps
to instruct the framework that you want a page to be server-side rendered.
This is the example of the list page where we load the contents from the CMS in a very straightforward way:
import React from 'react';
import { contentChef } from '../services/contentChefClient'
import { Card } from "../components/card";
import Layout from "../components/layout";
import Link from "next/link";
const Home = ({ topSites }) => (
<Layout
title={'ContentChef Top Sites'}
>
<div>
<h1 className="title">
Welcome to <a href="https://contentchef.io">ContentChef!</a> + <a href="https://nextjs.org">Next.js</a> tutorial
</h1>
<p className="description">
Top Sites
</p>
</div>
<div style={{ width: "80%" }}>
{topSites.map((site, index) => (
<Link
key={`top-site-${index}`}
href={'/top-site/[publicId]'}
as={`/top-site/${site.publicId}`}
>
<a style={{ textDecoration: 'initial' }}>
<Card
key={`top-site-${index}`}
image={contentChef.getImageUrl(site.payload.image)}
title={site.payload.title}
description={site.payload.description}
url={site.payload.url}
/>
</a>
</Link>
))}
</div>
</Layout>
);
//With this function we instruct Next to use SSR for this page!
export async function getServerSideProps() {
const result = await contentChef.searchContents();
return {
props: { topSites: result }
}
}
export default Home
Enable SSG for static routes
Now let's change the code to generate a static version of the same website!
We will start from the list page, which will be fairly easy. To instruct the framework to generate the page at build time, you need to export a function named getStaticProps
, and that's all!
So let's change the code accordingly in the index page above.
//SSR version
export async function getServerSideProps() {
const result = await contentChef.searchContents();
return {
props: { topSites: result }
}
}
//And Just rename it!
export async function getStaticProps() {
const result = await contentChef.searchContents();
return {
props: { topSites: result }
}
}
And now verify that's working with a build.
npm run build
And let's look the output in console:
Tada! The home page list is now static! But we have not finished yet. We want to create a static version of all the pages, including the detail pages, but now we see they're deployed as a lambda.
Enable SSG for dynamic routes
This step's a little bit trickier because we need to deal with the dynamic nature of the number of pages that we want to generate from our headless CMS, one for each website detail page.
To do that, we need to implement the getStaticProps
function for the single page and also add a getStaticPaths
function to tell to NextJs the paths we want to be generated.
Let's see the code to implement in ./pages/top-site/[publicId].js
file by opening it and removing the old getServerSideProps
.
We start by defining the getStaticPaths
function, to read the list of contents PublicIDs from the Delivery API and creates a list of "Paths" to be processed.
export async function getStaticPaths() {
const sites = await contentChef.searchContents();
const publicIds = sites.map(site => site.publicId);
const paths = publicIds.map(publicId =>{
return {params:{ publicId:publicId } }
});
return {
paths:paths,
fallback: false
};
}
Now, adding the getStaticProps
function is pretty straightforward and similar to the one used for the list, we just need a content PublicID to fetch it from ContentChef.
export async function getStaticProps({ params }) {
const result = await contentChef.getContent(params.publicId);
return {
props: {
topSite: result.payload,
cloudName: result.requestContext.cloudName
}
}
}
Let's try it by regenerating it:
npm run build
npm run start
And check again the console output:
Yahoo! All the pages are now static and you can browse them at http://localhost:3000
Triggering builds on a CI/CD pipeline
As you can see, generating SSR or SSG sites with next and a Headless CMS like ContentChef is fast and straightforward. With ContentChef, you can also easily add webhooks, so when you publish new content is easy to trigger a rebuild and redeploy of your site on CI/CD.
Why not give ContentChef and NextJS a try? Sign up for a 30-day trial and experience the benefits of a headless CMS for SSR and SSG for yourself!
Did you like our article?
Ready to accelerate content management?
Learn how you can get the most out of ContentChef.