Published on

A road to Contentful and Next.js

Authors
Contentful Website

Overview

With the help of this article, you'll be able to render a fully dynamic Contentful page into Next.js

Live demo: https://contentful-nextjs-tailwind.vercel.app/

Table of Contents

Choosing the right tech stack

For this starter kit we'll be using Next.js as a React framework, Vercel as a host, Tailwind as a CSS framework, and the Contentful SDK to fetch the CMS information.

Setting up Contentful

Contentful offers you a Community license that you can use for personal sites, hackathons, or philanthropic projects. The sign-up process is pretty straightforward.

We'll need to create 2 Content Types only. The first one will be the Hero content type.

Hero

Content type ID: hero

  • Title, short text. This will be entry title.
    • Field ID: title
  • Description, short text.
    • Field ID: description
  • CTA Text, short text.
    • Field ID: ctaText
  • CTA Link, short text.
    • Field ID: ctaLink

Your Contentful model should look like this:

hero model

Page

This model will refer to each website page. We only need 3 properties to get started.

Content type ID: page

  • Title, short text. This will be entry title.
    • Field ID: title
  • Slug, short text.
    • Field ID: slug
  • Components, references, many.
    • Field ID: components
page model

Entries

Now that we have our models, we need to create 2 entries. The first one is pretty easy. Just create a new Hero and fill all the properties.

For the homepage, make sure your entry looks like this:

page entry

Api keys

Finally, our last step in Contentful. Go to your settings and generate a Content Delivery Access Token.

Setting up the project

Luckily for you I've created a github repository with all the code that you need for running the project.

We'll be using degit to clone the repository.

npx degit https://github.com/Fedeorlandau/contentful-nextjs-tailwind.git myApp

Now go into your app and create a .env file with the following content. Please replace the ***** with your actual api keys. You can find all of this information in your Contentful settings.

CONTENTFUL_SPACE_ID='*****'
CONTENTFUL_TOKEN='******'
CONTENTFUL_ENVIRONMENT='*****'
NEXT_USE_SSR=1    

Now we need to install all the modules.

yarn

Finally, run your solution by doing

yarn dev

How page generation works

In the scripts folder you'll see the following script:

scripts/copySlug.js
const fs = require('fs')

require('dotenv').config()

if (process.env.NEXT_USE_SSR === '1') {
  fs.copyFileSync(
    './src/templates/[[...slug]].ssr.tsx',
    './src/pages/[[...slug]].tsx'
  )
} else {
  fs.copyFileSync(
    './src/templates/[[...slug]].ssg.tsx',
    './src/pages/[[...slug]].tsx'
  )
}

This will validate if you want to use regular SSR or SSG. Both are supported by Next.js. You only need to change the flag on the .env file.

After the validation it'll copy a template into the pages folder.

src/pages/[[...slug]].tsx
import { GetServerSideProps } from 'next'

import Page, { PageProps } from '../components/Page/Page'
import { getPageBySlug } from '../libs/contentful'

export default Page

export const getServerSideProps: GetServerSideProps<PageProps> = async (
  context
) => {
  let slug = context.params?.slug || '/'

  if (slug && typeof slug !== 'string') {
    slug = `${slug.join('/')}`
  }

  const page = await getPageBySlug(slug)

  return {
    props: {
      page: page.fields,
    },
  }
}

We're fetching the page by slug from Contentful. After we fetch the entry we return the fields as a prop for our Page component.

Our Page component iterates over the components property and finds the matching react component in our app.

src/components/Page/Page.tsx
import type { PageFields } from '../../types'
import ComponentMap from '../ComponentMap'

export interface PageProps {
  page: PageFields
}

export default function Page({ page }: PageProps): JSX.Element {
  return (
    <>
      {page.components?.map((component, index) => {
        if (component.sys.contentType.sys.id) {
          const Component = ComponentMap.get(component.sys.contentType.sys.id)
          return <Component {...component.fields} key={index} />
        }
      })}
    </>
  )
}

Finally, the Hero component gets created thanks to our ComponentMap.ts file. This will use the Content type ID hero as a key and the Hero component as a value.

src/components/ComponentMap.ts
import Hero from './Hero/Hero'

const ComponentMap = new Map()

ComponentMap.set('hero', Hero)

export default ComponentMap

Deploying to vercel

This starter kit is compatible with Vercel. You only need to set up the project and remember to put your Contentful API keys in.

Github repo and Lighthouse score

Feel free to check out the Github repo where you can find the source code of this project. This example provides the following Lighthouse score to get started. Pretty cool, isn't it?

lighthouse score