Getting started with Next.js and GraphCMS

Getting started with Next.js and GraphCMS

In this tutorial, we'll learn how we can create a Next.js project and integrate it with GraphCMS.

Introduction

GraphCMS's mission is to remove traditional content management pain points by using the power of GraphQL, and take the idea of a Headless CMS to the next level.

GraphCMS provides a lot of features out of the box. Some of them are as follows:

  1. GraphQL API
  2. Content Localization
  3. Webhooks
  4. Versioning
  5. Content API

It also provides a lot of Editorial and Operational features. Some of them are as follows:

  1. Rich Content Editing
  2. Global Marketing Campaigns
  3. SEO Friendly
  4. WYSIWYG
  5. Built in Localization

A demo of the application that we will be building is available here which is hosted on Vercel and the source code is available on Github.

Getting Started with Next.js

Let's setup a new Next.js project. To do so, we will have using the following command:

npx create-next-app
# or
yarn create next-app

Bootstrapping a Next.js project

The above command will generate the following files for us:

Directory structure for Next.js project

Now, we can go inside the directory Next.js created for us and start the development server using the following command:

cd nextjs-graphcms && yarn dev

The above command will spin up a development server on http://localhost:3000/:

Development server for Next.js on http://localhost:3000/

Getting started with GraphCMS

First, we need to create an account on GraphCMS for free. If you already have an account, you can just sign in.

Next, after signing in, we can select from one of the available Project Templates. We will start with the Blog Starter template.

GraphCMS templates

Next, we need to add a name to our Project and click on the "Create Project" button:

Adding name to our GraphCMS templates

Next, we can select the "Personal" plan and click on the "Continue" button:

Selecting our GraphCMS project plan

We can find out the URL endpoint of our project from our project settings:

GraphCMS project settings

Now, if we visit the URL endpoint and start querying data using GraphQL, we won't get any data:

Fetching data from GraphCMS

This is because we haven't updated the API permissions to fetch both Draft and Published data from GraphCMS:

Updating project permissions on GraphCMS

Now, if we fetching GraphQL from our URL endpoint, we will get proper data:

Fetching both draft and published data from GraphCMS

Let's save the URL endpoint in a .env file in our application:

GRAPHQL_URL_ENDPOINT=https://api-eu-central-1.graphcms.com/v2/something/master

Installing GraphQL client

We will be using graphql-request which is a minimal GraphQL client supporting Node and browsers for scripts or simple apps.

We can install that package using the following command:

npm add graphql-request
# or
yarn add graphql-request

Now, let's restart our Next.js server using yarn dev. We can use graphq-request to fetch data from our URL endpoint. To do so, we need to modify our pages/index.js file:

import { GraphQLClient } from "graphql-request";

const graphcms = new GraphQLClient(process.env.GRAPHQL_URL_ENDPOINT);

export async function getStaticProps() {
  const { posts } = await graphcms.request(
    `
    query Posts() {
      posts {
        id
        title
        excerpt
        slug
        coverImage {
          id
          url
        }
        author {
          id
          name
        }
        date
      }
    }
  `
  );

  return {
    props: {
      posts,
    },
  };
}

export default ({ posts }) => {
  return (
    <>
      {posts.map((post) => {
        return (
          <div key={post.id}>
            <img src={post.coverImage.url} alt={post.title} />
            <h2>{post.title}</h2>
            <p>{post.date}</p>
            <p>{post.author.name}</p>
            <p>{post.excerpt}</p>
          </div>
        );
      })}
    </>
  );
};

Now, if we visit http://localhost:3000/, we should be able to see the following screen:

Showing data from GraphCMS

Adding styles using TailwindCSS

We will be adding TailwindCSS using CDN. However, that is not the recommended way of installing TailwindCSS. We are using that just for the sake of decreasing the size of this tutorial. More information regarding how to install TailwindCSS can be found in their official documentation.

We need to create a new file _app.js inside our pages directory and add the following code:

import Head from "next/head";

const App = ({ Component, pageProps }) => {
  return (
    <>
      <Head>
        <link
          href="https://unpkg.com/tailwindcss@^1.0/dist/tailwind.min.css"
          rel="stylesheet"
        />
      </Head>
      <Component {...pageProps} />
    </>
  );
};

export default App;

Now, if we restart our server, we should be able to see that TailwindCSS is getting downloaded.

Integrating TailwindCSS with out project

Next, we need to modify our pages/index.js file with the following code:

export default ({ posts }) => {
  return (
    <div className="py-16 bg-gray-100">
      {posts.map((post) => {
        return (
          <div
            key={post.id}
            className="max-w-lg shadow-lg mb-16 rounded-lg mx-auto flex"
          >
            <div
              className="h-48 lg:h-auto lg:w-48 flex-none bg-cover rounded-t lg:rounded-t-none lg:rounded-l text-center overflow-hidden"
              style={{ backgroundImage: `url(${post.coverImage.url})` }}
              title={post.title}
            ></div>
            <div className="bg-white rounded-b lg:rounded-b-none lg:rounded-r p-4 flex flex-col justify-between leading-normal">
              <div className="mb-8">
                <div className="text-gray-900 font-bold text-xl mb-2">
                  {post.title}
                </div>
                <p className="text-gray-700 text-base">{post.excerpt}</p>
              </div>
              <div className="flex items-center">
                <div className="text-sm">
                  <p className="text-gray-900 leading-none">
                    {post.author.name}
                  </p>
                  <p className="text-gray-600">{post.date}</p>
                </div>
              </div>
            </div>
          </div>
        );
      })}
    </div>
  );
};

Now, if we visit http://localhost:3000/, we should be able to see the following:

Cards design using TailwindCSS for our Next.js project

We've successfully fetched data from GraphCMS and rendered them on the browser.

Creating separate pages for each blog post

We can also show blogs in separate routes. To do that, we will need to modify our pages/index.js file:

import { GraphQLClient } from "graphql-request";
import Link from "next/link";

const graphcms = new GraphQLClient(process.env.GRAPHQL_URL_ENDPOINT);

export async function getStaticProps() {
  const { posts } = await graphcms.request(
    `
    query Posts() {
      posts {
        id
        title
        excerpt
        slug
        coverImage {
          id
          url
        }
        author {
          id
          name
        }
        date
      }
    }
  `
  );

  return {
    props: {
      posts,
    },
  };
}

export default ({ posts }) => {
  return (
    <div className="py-16 bg-gray-100">
      {posts.map((post) => {
        return (
          <Link key={post.id} as={`/post/${post.slug}`} href="/post/[slug]">
            <a className="max-w-lg shadow-lg mb-16 rounded-lg mx-auto flex">
              <div
                className="h-48 lg:h-auto lg:w-48 flex-none bg-cover rounded-t lg:rounded-t-none lg:rounded-l text-center overflow-hidden"
                style={{ backgroundImage: `url(${post.coverImage.url})` }}
                title={post.title}
              />
              <div className="bg-white rounded-b lg:rounded-b-none lg:rounded-r p-4 flex flex-col justify-between leading-normal">
                <div className="mb-8">
                  <div className="text-gray-900 font-bold text-xl mb-2">
                    {post.title}
                  </div>
                  <p className="text-gray-700 text-base">{post.excerpt}</p>
                </div>
                <div className="flex items-center">
                  <div className="text-sm">
                    <p className="text-gray-900 leading-none">
                      {post.author.name}
                    </p>
                    <p className="text-gray-600">{post.date}</p>
                  </div>
                </div>
              </div>
            </a>
          </Link>
        );
      })}
    </div>
  );
};

We will also need to create a new file pages/post/[slug].js with the following content:

import { GraphQLClient } from "graphql-request";
import Link from "next/link";

const graphcms = new GraphQLClient(process.env.GRAPHQL_URL_ENDPOINT);

export async function getStaticProps({ params }) {
  const { post } = await graphcms.request(
    `
    query Post($slug: String!) {
      post(where: { slug: $slug }) {
        id
        title
        content{
          text
        }
        slug
        coverImage {
          id
          url
        }
        author {
          id
          name
        }
        date
      }
    }
  `,
    {
      slug: params.slug,
    }
  );

  return {
    props: {
      post,
    },
  };
}

export async function getStaticPaths() {
  const { posts } = await graphcms.request(`
    {
      posts {
        id
        title
        content{
          text
        }
        slug
        coverImage {
          id
          url
        }
        author {
          id
          name
        }
        date
      }
    }
  `);

  return {
    paths: posts.map(({ slug }) => ({
      params: { slug },
    })),
    fallback: false,
  };
}

export default ({ post }) => {
  return (
    <div className="py-16 bg-gray-100 min-h-screen">
      <div className="max-w-lg shadow-lg rounded-lg mx-auto mb-16">
        <div
          className="h-48 rounded-t flex-none bg-cover text-center overflow-hidden"
          style={{ backgroundImage: `url(${post.coverImage.url})` }}
          title={post.title}
        />
        <div className="bg-white rounded-b lg:rounded-b-none lg:rounded-r p-4 flex flex-col justify-between leading-normal">
          <div className="mb-8">
            <div className="text-gray-900 font-bold text-xl mb-2">
              {post.title}
            </div>
            <p className="text-gray-700 text-base">{post.content.text}</p>
          </div>
          <div className="flex items-center">
            <div className="text-sm">
              <p className="text-gray-900 leading-none">{post.author.name}</p>
              <p className="text-gray-600">{post.date}</p>
            </div>
          </div>
        </div>
      </div>
      <div className="max-w-lg mx-auto">
        <Link href="/">
          <a>Back to all posts</a>
        </Link>
      </div>
    </div>
  );
};

For more information regarding how the Link component works can be found in the official Next.js documentation.

More information regarding how to add pages in Next.js can be found in the official Next.js documentation.

Separate pages for blogs using the Link component

Conclusion

In this tutorial, we learnt how we can integrate GraphCMS with Next.js and create a blogging site. The demo of the application that we built as a part of this tutorial is available here which is hosted on Vercel and the source code is available on Github.