« Back to blog posts

Building a production ready website with GatsbyJS & Prismic.io

Building a production ready website with GatsbyJS & Prismic.io

GatsbyJS is one of my favourite open source frameworks for building static websites. Being able to take advantage of their plugin ecosystem to build rapid websites whilst using some of the latest tools such as GraphQL makes the experience both fun and simple. Prismic is a headless CMS with a powerful API which takes away the pain of managing rich content such as images, headings, lists etc which feature regularly in web content.

This website you're currently on is built on this exact technology stack, so we'll be using it as reference throughout.

Why Prismic?

The first iteration of our website was built without a CMS system. All content was managed from within the code, importing various assets / markdown files. This quickly became painful as the website started to grow, and required contributions to be made directly from within the repository.

The hunt for a headless CMS started with Contentful, however something just didn't click. Although the developer experience seemed good, the actual UI and handling of content block just seemed over-complicated. Nothing else really tickled my toes until I found Prismic. The simplistic UI and the ability to 'slice data' made complete sense to me.

Structuring GatsbyJS

This website is built with two page types: static and dynamic.

  • Static: Those pages which you're expecting and form the foundation of your website (homepage, about, contact).
  • Dynamic: Generated content, such as blog posts (like this one!).

Tackling static pages with Gatsby & Prismic is the easiest place to get started. We know our website structure, and it's a simple matter of adding "types" which define key areas of each page. Lets get started.

Static Pages

With a Prismic repository setup, we can jump across to the "Custom Types" section of the dashboard:

A type is "something" that we'll be able to query against to pull our data into the website. Lets go ahead and create a new "Homepage" type, as a "Single Type" (static page). A "Single Type" means we'll only be able to create content against it's schema once, which is exactly what we need.

Prismic gives us a variety of blocks to work with, from an image, rich text, date pickers and more. By adding these to our new Homepage type, we can build up the schema of exactly what's needed to populate our homepage. The homepage of this website has a number of areas to edit, from the hero to the about Invertase section. I simply created a number of "Title" and "Rich Text" named blocks which will correspond to that area on our page:

Once saved, head back over to the "Content" section of the Prismic dashboard. When creating a new piece of content, you'll be able to work on your Homepage type. Now it's a simple matter of filling out your content as you see fit. You may have noticed that on our homepage we have a few areas which aren't as they appear below, for example the technologies is a comma separated list, whereas the website shows it automatically cycling through each technology. This is where our code comes in to play; we'll pull that text out of Prismic as-is, then split it by commas to form generate our list to cycle through. Prismic provides the content, rather than the functionality.

Dynamic Pages

A dynamic post allows the user to be more creative with the content. A blog post for example requires the end user to submit a slug, the rich content and maybe a few other nice-to-haves such as a hero image, author name etc.

Within Prismic, create a new Custom Type as a "Repeatable Type" called blog_post. You'll be able to create multiple content articles using the schema of this type. For example, our blog post Custom Type is setup as follows:

Each section is clearly labelled for it's purpose, however the content section is simply a rich text block waiting for the user to start writing their content. We also leave the slug field up to the user.

Back in our Content section of the dashboard, we can now create our own blog post... here's the one I'm currently working on (blogception right here):

Sourcing Prismic content with GatsbyJS

GatsbyJS allows us to source data, which is a fancy way of saying it creates a GraphQL schema from our data.

Prismic provides an API to pull our content from a client. Now, we could go about this ourselves using various API requests to grab our content and add it to the GraphQL database, however we've luckily got access to a gatsby-source-prismic which takes care of that work for us.

Go ahead and install the plugin, adding it to your gatsby-config.js file, grabbing the API key from your Prismic dashboard. Start the Gatsby development server (gatsby develop) and the plugin will download and source the created content into GraphQL for us - pretty neat!

The plugin populates the database with all of our content with the prefix prismic. Open the GraphQL explorer Gatby hosts at http://localhost:8000/___graphql

Lets access the homepage data, querying the prismicHomepage node the plugin has created for us:

We can now extend this query to grab any additional fields, e.g. about. Whether you use text or html is your call, however where the HTML schematics is important, it's better to use the text field and control HTML yourself.

Creating static pages: useStaticQuery

Using React 16.8+, we're able to take advantage of React Hooks. GatsbyJS exposed a function called useStaticQuery which allows our pages to statically query data which is injected into our component for consumption. If you're using a lower version of React, the StaticQuery component has you covered.

Create a new file in pages/index.js within your Gatsby project. Gatsby will automatically generate a new page at the root of your website (the homepage). In that file, we can now query our data using, and inject it into our website:

import React from 'react';
import { graphql, useStaticQuery } from 'gatsby';

export default function Homepage() {
  const { homepage } = useStaticQuery(graphql`
    query {
      homepage: prismicHomepage {
        data {
          heading {
            text
          }
        }
      }
    }
  `);

  return <div>{homepage.data.heading.text}</div>;
}

You will now find the content pulling through to your page. Now it's just a matter of building out your website in usual React fashion and querying the data as and when it's needed. This works create for static content that we are expecting, and you're even able to inject rich content into the website by passing the html output from Prismic into React's dangerouslySetInnerHTML.

Creating dynamic pages: createPage

A website containing dynamic content such as blog posts means you're not able to statically query the page data, since we're not exactly sure what pages or content is going to be created. Dynamic pages commonly use a repeatable page template too. Rather than manually adding a new pages/blog/my-new-post.js file and querying the same data schema for every post, we can instead create these pages during Gatsby's build process using the createPage action.

Go ahead and open (or create) the gatsby-node.js file in the root of your project. We're going to take advantage of the gatsby-source-prismic plugin and the createPages API from Gatsby. As we have seen, the plugin creates "nodes" of data and Gatsby transforms them into a GraphQL schema which we can query. The createPages API runs after the GraphQL schema has been created and allows us to create our own pages as the name suggests.

We previously made a blog_post type, which our plugin also knows about. It's taken the data for this type and created BlogPost nodes, which we can query with allPrismicBlogPost:

Using this data, we can now pragmatically create pages! Add the following to the gatsby-node.js file:

exports.createPages = async ({ reporter, actions, graphql }) => {
  const { createPage } = actions;
  const blogTemplate = path.resolve('src/templates/blog-post/index.js');

  // Query our blog posts
  const result = await graphql(`
    {
      posts: allPrismicBlogPost {
        edges {
          node {
            id
            uid
          }
        }
      }
    }    
  `);

  if (result.errors) {
    reporter.panic(result.errors);
  }

  result.data.posts.edges.forEach(({ node }) => {
    // Create a page for each blog post
    createPage({
      path: /blog/${node.uid},
      component: blogTemplate,
      context: {
        id: node.id,
      },
    })
  });
};

The uid is our unique identifier we defined within Prismic which we use to generate the page slug, and the id is the internal GatsbyJS generated ID for our data node. The id is passed into the context which we can use to query the node data within our page template.

Above we defined a page template within src/templates/blog-post/index.js, this will be used to for all of our blog posts. We now need to create that file, and return a React component:

import React from 'react';

function BlogPost() {
  return (
    <>
      <h1>Blog post title</h1>
      <article>Blog post content</article>
    </>
  );
}

export default BlogPost;

Rerunning gatsby develop will now create new blog posts with a unique slug for each, however they'll all return the same component as we just defined. In the previous step we passed the node id to content. We are able to perform a page query, which exposes any context values we passed through to query with. Using the generated node id, lets query the blog post content:

import React from 'react';
import { graphql } from 'gatsby';

function BlogPost() {
  // ..
}

export const pageQuery = graphql`
  query BlogPost($id: String!) {
    prismicBlogPost(id: { eq: $id }) {
      data {
        title {
          text
        }
        content {
          html
        }
      }
    }
  }
`;

export default BlogPost;

This page query will be queried on every page creation with the context, and the result of the entire query will be passed through as the data prop to our component. Now it's a simple matter of taking our Prismic content and building a page with React:

function BlogPost({ data }) {
  const { title, content } = data.prismicBlogPost.data;

  return (
    <>
      <h1>{title.text}</h1>
      <article dangerouslySetInnerHTML={{ __html: content.html }} />
    </>
  );
}

Summary

In summary, both Gatsby and Prismic provide a flexible ecosystem for building websites without having to worry about dealing with rich content and user access/knowledge. Prismic doesn't just have to be used for mapping content directly to a website, the flexible content and type system means you can store any data and use it in whatever way you see fit - for example storing list of product IDs and querying those IDs during the Gatsby build process to create an order-able product listing.


Share this blog post: