DEV Community

Cover image for How to Connect Contenful and Next.js: Finding A CMS Part 2
James 'Dante' Midzi
James 'Dante' Midzi

Posted on • Originally published at dantedecodes.hashnode.dev

How to Connect Contenful and Next.js: Finding A CMS Part 2

Let's continue our journey to find which CMS best fits our needs.

Today we take a look at Contentful

What is Contentful?

Contentful describes itself as the API first content management system. It makes use of what it calls Spaces - a sort of hub for your project and its content. With it, you can create your models and relationships between them if you so choose.

In addition to managing your content, it offers additions to your Contentful space that streamline working with your content.

Contentful's interface is very nice to look at and work in.

Here is what they say:

Launch faster with a modern content platform
It’s the easiest, fastest way to manage content: Integrate your tools. Publish across channels. Unblock your team with our industry-leading app framework.

HOUSEKEEPING

Again, this is a proof of concept article. I am not explaining every single thing - this is also not a beginner walkthrough. I will gladly explain further if you ask me to.

Also, I did not show screenshots of the pulled data rendered in the Next app, you have already seen those. There is no point in showing them again. Sanity and NextJS

This time we are going to get everything in Contentful ready before we touch out Next app.


Setup Contentful

Head over to Contentful and create your account or login.

After you sign up you will be taken to your space - where your project lives.

space.png

When you first create your account you are given an opportunity to try out their app platform for 60 days.

Create Content Models

Select Content Model on the top bar then select Add content type.

content type.png

We'll make a Post and Author model.

Author

We will add fields to the type.

author.png

You have several settings you can set for each field.

options.png

Post

We will add fields to this model also.

article.png

In addition, we will add a reference to the Author model.

ref.png

Create Author and Posts

Now select the Content option in the top bar. Create an author and a couple of posts.

post.png

Generate API key

We then need a way to communicate with our space. Select Settings in the top bar, then API keys.

key.png

Then choose Add API key. Name your key and save it. Copy these two things and save them somewhere, we'll need them later:

  • Space ID
  • Content Delivery API - access token

Invite Members

To collaborate on Contentful, you have to invite members to your space. Select your organisation name then Organization settings and subscriptions.

members.png

Select Users in the top bar and then Invite users.

invite.png

Features

Within Contentful, you have a choice of several Apps you can add to your space to enable extra functionality. You can find these by selecting Apps in the top bar.

apps.png

For example:

Compose: Which enables the easy creation and management of content.

Launch: Which brings confidence and speed in content delivery.

Plans

Your Contentful plan is to do with how many spaces you can have and the features in said space. Basically, they are divided into Community, Teams and Enterprise.

Here is a link to their pricing page.

With Contentful all setup, we can move on to our Next app.


Setup Next

Just like last time we need my starter. Clone it:

git clone https://github.com/Psypher1/next-cms-starter.git
Enter fullscreen mode Exit fullscreen mode

Navigate into it and install the dependencies:

cd next-cms-starter

npm install
Enter fullscreen mode Exit fullscreen mode

Install Dependencies

We will need two packages for this build:

To communicate with Contentful

npm install contentful
Enter fullscreen mode Exit fullscreen mode

To render rich text

npm install @contentful/rich-text-react-renderer
Enter fullscreen mode Exit fullscreen mode

This time I'll be following some best practices

Create env files

In the root of the Next project, make a file called .env.local, we will place the API key and token we generated in it:

/* .env.local */
CONTENTFUL_SPACE_ID=
CONTENTFUL_ACCESS_TOKEN=
Enter fullscreen mode Exit fullscreen mode

Make a folder called utils or lib. Create a file called contentfulClient.js and place this in it:

/* contentfulClient.js */

import { createClient } from "contentful";

const client = createClient({
    space: process.env.CONTENTFUL_SPACE_ID,
    accessToken: process.env.CONTENTFUL_ACCESS_TOKEN,
});

export default client;
Enter fullscreen mode Exit fullscreen mode

Now we can move on to pulling our data.

Load Post List on Blog Home Page

Navigate to the blog folder and into the index.js file. Place this in it:

/* ..src/pages/blog/index.js */

import client from "../utils/contentfulClient";

/* make async call to fetch posts from Contentful */

export async function getStaticProps() {
    const res = await client.getEntries({ content_type: "post" });
    const data = await res.items;

    return {
        props: {
            posts: data,
        },
    };
}
Enter fullscreen mode Exit fullscreen mode

Here is an example of the structure of the data we pull from contentful:

data 2.png
The structure makes GraphQL a better option for querying the data

Now we load the data onto the page:

export default function Blog({posts}){

    return {
        <div>
            {posts && posts.map(post =>(
                <div key={post.sys.id}>
                    <Link href={`/blog/${post.fields.slug}`}>
                        <a>{post.fields.title}</a>
                    </LInk>
                    <img src={post.fields.thumbnail.fields.file.url}>
                </div>
            ))}
        </div>
    }

Enter fullscreen mode Exit fullscreen mode

I intentionally chose to pull the data this way to illustrate potential errors you can encounter due to the way the data from contentful is structured. There is a lot of chaining with this method.

NOTE: If you choose to use the Nextjs inbuilt Image Component - which I highly suggest you do - you will encounter an error about the source needing to be an absolute URL. In that case, just prefix the src with https: in template literals.
Instead of:

src={post.fields.thumbnail.fields.file.url}` 
Enter fullscreen mode Exit fullscreen mode

do

src={`https:${recipe.fields.thumbnail.fields.file.url}`}
Enter fullscreen mode Exit fullscreen mode

NOTE: When you use the Image component you will need to tell Next about the URL you are getting the images from.

In the root of your project, make a file called next.config.js and add this to it:

/* next.config.js */
module.exports = {
  images: {
    domains: ["images.ctfassets.net"],
  },
};
Enter fullscreen mode Exit fullscreen mode

Load Individual Posts

Now we can move on to the slug page. Navigate to [slug].js and add the following to it:
We start by generating the paths:

/* ..src/pages/blog/[slug].js */

import client from "../../utils/contentfulClient";

import { documentToReactComponents } from "@contentful/rich-text-react-renderer";

export async function getStaticPaths() {
    const res = await client.getEntries({
        content_type: "post",
    });

    const paths = res.items.map((item) => {
        return {
            params: { slug: item.fields.slug },
               };
    });


 return {
     paths,
    // if we go to path that does not exist, show 404 page
    fallback: false,
    };

}
Enter fullscreen mode Exit fullscreen mode

Then retrieve the post with the matching slug:

export async function getStaticProps({ params }) {
    const slug = params.slug;

    const res = await client.getEntries({
        content_type: "post",
        "fields.slug": slug,
    });

    // this returns a list
    const data = await res.items;
    //we only want the first item in that list
    const post = data[0];

    return {
        props: {
            post,
        },
    };
}
Enter fullscreen mode Exit fullscreen mode

Then we render the individual post to the page:

/* load post data onto page*/
export default function PostDetail({ post }) {
    /*destructure data for easier output*/
     const { coverImage, title, body, author} = post.fields;
    const image = coverImage.fields.file.url;

    return(
      <div>
        <h2>{title}</h2>
        <img src={`https:${image}`} alt={title} />

        // author info
         <div>
            <p>{author}</p>
        </div>
        // post body
         <hr />
        <div >
            {/* use the package to render post body*/}
            {documentToReactComponents(body)}
        </div>

        // useful links
        <hr />
        <br />
        <div className={styles.links}>
           <Link href="/blog">
                 <a className={styles.back}> {"<Back to Blog"}</a>
           </Link>
           <Link href="/">
             <a className={styles.back}> {"<Home"}</a>
          </Link>
       </div>
    </div>
    )
}
Enter fullscreen mode Exit fullscreen mode

I destructured the data we pulled for two main reasons:

  1. To avoid the chaining we did on the index page
  2. To make the code a bit more readable.

Extra Info

in addition to the GraphQL Content API, Contentful also has an Images API. Both of which I will be looking into later.

Contentful also has a Developer Portal that you can use to learn more about the CMS.

My Advice for Working with Contentful

  • Use GraphQL to fetch your data: As you have seen, when we fetch from contentful, the structure of the data is a bit hard to work with.
  • Try if you can to get your images in the dimensions you want to use them.

Scoring

Are we ready for the verdict? How does Contentful fare?

1. Ease of Use

For me
Contentful, for the most part, is simple to use. A few clicks here and there and my data was ready to be consumed.

For user
The user really liked the interface more than they did Sanity. They enjoyed the experience more

2. How Well Data Can Be Queried

I believe I have mentioned querying data several times. Doing it straight from Contentful will lead to unnecessary chaining.

It is very helpful that they offer an alternative - in the form of GraphQL

3. Ease of integration with Next

Another happy instance for me again. It wasn't complicated at all, just make get your access token loaded and you're off to the races.

4. Time Taken To Production

The moment you publish your content it is already live and ready to be fetched. All that remains is your frontend - pushing to Github and deploying to Vercel or Netlify

5. Support/Community

They have a Slack. But after the resounding support I received from the Sanity Slack, it leaves a lot to be desired.

There is a bot on their site - I don't like these kind of bots. it kept pressing me for a work email - leading me to the assumption that Contentful doesn't cater to individuals that well.

6. Ease of Deployment

No deployment is necessary, it's all live when you hit publish.

Conclusion

Contentful was interesting to work with. Simple and user friendly. A better job could be done in terms of support though.


If you followed along - Thank You - and came across any issues, please feel free to ask me. I will do my best to help you.


Thank you for reading, let's connect!

Thank you for visiting this little corner of mine. Let's connect on Twitter and LinkedIn

Top comments (0)