Building a portfolio site with Contentful, Next.js and Netlify

When I wanted to make my portfolio site fast and easy to maintain, I landed on the following solution to create a static site using Contentful as my content manager, Next.js to display the data, and Netlify for hosting.

Requirements of the site

  • Fast ⚡️
  • Secure 🔒
  • Maintainable 🏗
  • Easy to deploy 🚀
  • Service Worker ⚙️

Goal

By the end of this you should have a good understanding of how I got to this website:

Example website we're about to build

Contentful

With an empty space, we are going to start to add our content types:

Contentful: Create a new space then add content types

The main focus on this site is the work items, so we’ll go ahead and add a new content type called Work.

Create a new content type

Next, we need to add the fields into the work content type. But what fields exactly do we need to add? Let's open up that previous build of the site.

Adding a new field

If we look back at the previous build of the site, we can check what fields we need for our content type:

Refer to the previous build of the site

Looks like we will need:

  • Featured image
  • Logo
  • Title
  • Description
  • Colour
  • Link

Now go ahead and add those into Contentful:

Adding fields needed for the content model

Now that we have our content model setup, we can go through, create and fill out all of the work posts in the content area:

Posts in content area

One more thing to note, I have actually created a custom content model for the Homepage. This has a title and then just a Work field which has a 'many reference' attribute.

Custom content model for homepage

This only exists so I can do some sorting with the nice drag-and-drop functionality that Contentful allows:

Drag-and-drop UI in Contentful

Now we have all our content in Contentful, let’s head over to our command line and code editor, open up a new project to grab the content and create some React components to display it.

Next.js

We can create a new project, via the command line, and then move things into it.

1
mkdir new-thing && cd new-thing

I’m going to open up my code editor and drop in a package.json file with an empty object inside so when we run the next command, the dependencies will actually save to that new package.json file.

Following that, a visit to the Next.js repo lets me find the next command to get moving:

1
npm install --save next react react-dom

I’m going to run that script to install the dependencies; that will also automatically save those dependencies to my package.json file.

Installing dependencies

I’ve gone ahead here, put in the scripts to build for production, and develop the project locally.

1
2
3
4
5
6
7
{
  "scripts": {
    "dev": "next",
    "build": "next build",
    "start": "next start"
  }
}

Putting scripts into production

That’s it for configuration—we are ready to create a page, and then create a few components to put on that page!

We will start by creating a folder called pages, which Next.js recognises as a route, and then creating our index page inside that folder; that will be our homepage.

1
mkdir pages && touch pages/index.js

Creating a folder called pages

Now that we have that, let’s run the project locally and visit localhost:3000 in the browser:

1
npm run dev

Viewing the project in the browser

Great, now we need to get our Contentful data—I have a little script I’m going to run which will request that from Contentful and then write it to disk. The reason I’m doing it this way is to make everything as fast as possible by just reading JSON straight from disk; this also helps with caching later.

This snippet can be found here.

getcontent.js file

We need to update our package.json file at this point so we are able to run our postinstall script.

1
2
3
4
5
6
7
8
9
{
  "scripts": {
    "dev": "next",
    "build": "next build",
    "start": "next start",
    "postinstall": "npm run getcontent",
    "getcontent": "babel-node helpers/getcontent.js install"
  }
}

Update the package json file to run the postinstall script

We need to use a custom .babelrc file here to utilize the import / export tokens available to us in that getcontent.js file.

1
2
3
4
5
6
{
  "presets": [
    "env",
    "next/babel"
  ]
}

Use a custom babelrc file

Create a new folder from the root of the project for the JSON file to be written to—we will call that data:

1
mkdir data

The last step here before we can run our postinstall script would be installing the depedencies:

1
npm i --save babel-cli contentful

Phew, ok, let’s run it!

1
npm run postinstall

Excellent, we have data from Contentful, written to JSON locally:

Data from Contentful written to a local JSON

Now, we will display this data using a few React Components. To do that, let's create a components folder, enter it, and create the three main components we will be using:

1
mkdir components && cd components && touch WorkFeed.js && touch WorkItem.js && touch BackgroundImage.js

Create a components folder and create the three main components, Workfeed, Workitem and backgroundimage js

Back to index.js, let's render our WorkFeed component and give it the data from Contentful:

1
2
3
4
import { Component } from 'react'
import data from './../data/pageHome'
import WorkFeed from './../components/WorkFeed'
export default () => <WorkFeed data={data.work} />

Inside WorkFeed, we will loop our data and render a WorkItem for every case-study we have:

1
2
3
4
5
6
7
8
import WorkItem from './WorkItem'
export default ({ data }) => (
  <section className='work-feed'>
    {data.map((item, i) => {
      <WorkItem key={i} item={item} />
    })}
  </section>
)

Now inside the WorkItem, we move on to render all of our data. This component could actually be broken up into smaller chunks, but as with all personal projects, sometimes you don’t have time to make something perfect:

This snippet can be found here.

Rendering all the data

I’ve left out some of the styling here for presentation purposes.

Let’s head back to the browser and hit refresh—we should have some data shown in the manner we would like it displayed:


Last step on Next.js is to build the project and export this site as a static site (I’ve added in another pull from Contentful, in case anything in our system has changed):

1
npm run postinstall && next build && next export

This export command will output our site into a directory called out, which will be important in the next step.

Netlify

Alright, now that we have what we want on the project side, let’s get the site deployed to Netlify. I’ve already version-controlled this site and connected my Bitbucket account to Netlify, which has gone and found the repo for me:

Creating a new site on Netlify

Now I can input my build command and publish directory, and hit deploy to get this site live.

Deploying the site live

Here we have it!

What the site looks like

Netlify + Contentful

At this point, I'd like to show you how to auto-deploy to Netlify whenever we have published or updated anything from inside Contentful.

Let’s head back to Netlify to create a new ‘Build hook’:

Create a new build hook

Hit save:

Save the build hook

Excellent, now we have a URL for running deployments from Netlify, which we can copy and place into Contentful.

Let’s head over to Contentful to set up a webhook which will post to the URL we just generated from Netlify:

Set up a webhook that will post to the Netlify-generated URL

Hit save on that, and we now have our webhook setup to push to Netlify. Let’s give that a quick test:

Testing the webhook setup: Editing content

Testing the webhook setup: Deploying content

Testing the webhook setup: Viewing the site

Perfect!

That’s a quick run-down of how to get a portfolio (static) site up and running, using the combination of Contentful, Next.js and Netlify to produce a great static site.

Thanks for reading!

Blog posts in your inbox

Subscribe to receive most important updates. We send emails once a month.