Using Prismic with Gatsby

This article discusses how to use Prismic with Gatsby through the use of the GraphQL based plugin

Gatsby is a Static Progressive Web App Generator that allows developers to build performance oriented web sites from a wide variety of data sources. Prismic is of course one of them, bringing powerful features such as Previews and Release scheduling to the strengths of working with a modern scalable framework. There are however some aspects to consider when integrating Prismic into Gatsby, such as fetching data, creating pages programmatically and most exciting of all, previewing pages that are normally not accessible when building.


Using the Gatsby source plugin

Gatsby uses plugins, packages that source and transforms data, to extend what their generated web apps can do or get data from. Using a community produced plugin it is possible to access data from Prismic in a format that allows their queries to work properly, while still allowing regular queries to be passed through directly to the Prismic API if needed. Due to these particular needs, the repository you're using will require to have the GraphQL functionality enabled. If you are unsure that you have this feature enabled, feel free to contact us through the support chat system.

To use this plugin, it's a matter of installing it to your existing Gatsby project.

Copy
$ npm install --save gatsby-source-prismic-graphql prismic-reactjs

After installing, you will have to configure the project to work with your Prismic repository and your custom types. Build a link resolver first as a file /src/utils/linkResolver.js, so it can be registered as part of the configuration. In the gatsby-browser.js file, add the following:

Copy
const { registerLinkResolver } = require('gatsby-source-prismic-graphql');
const { linkResolver } = require('./src/utils/linkResolver');

registerLinkResolver(linkResolver);

In the gatsby-config.js file you can proceed in configuring the plugin for your specific Prismic repository.

Copy
plugins: [
  {
    resolve: 'gatsby-source-prismic-graphql',
      options: {
        repositoryName: 'your-repo-name', // (REQUIRED, replace with your own)
        accessToken: '##########', // (optional API access token)
        path: '/preview', // (optional preview path. Default: /preview)
        previews: true, // (optional, activated Previews. Default: false)
        pages: [{ // (optional, builds pages dynamically)
        type: 'Article',         // TypeName from prismic
        match: '/article/:uid',  // Pages will be generated under this pattern
        path: '/article',        // Placeholder page for unpublished documents
        component: require.resolve('./src/templates/article.js'),
      }],
    }
  }
]

Take note of the different fields. We will go into detail on the optional features, such as dynamic page generation from a slug like UID, and previews, but for simply fetching data from a Prismic source all you need is to specify the repository name.


Fetching data with queries

With the plugin configured, you can now access your Prismic as one of the data sources in your Gatsby web app. You can start your development server with the command gatsby develop from terminal in your project folder. By default you can access the GraphiQL interface by browsing to localhost:8000/___graphql. Here you can run GraphQL queries of all the data accessible to Gatsby. Run a simple query to check that the sourcing is working correctly.

Copy
{
  prismic {
    _allDocuments {
      edges {
        node {
          __typename
        }
      }
    }
  }
}

This query will return the Custom Type of all the Documents currently published in your repository. This tool provides a good method for building the GraphQL queries to fetch the specific data fields that you require to render in any given page. Once satisfied with the query, you have to include this query in the /pages file you're creating.

As an example, for a homepage defined in /pages/index.js:

Copy
export const query = graphql`
{
  prismic {
    allHomepages {
      edges {
        node {
          title
          description
        }
      }
    }
  }
}
`


Rendering fetched data

Accessing the data returned by the query is fairly straightforward. For example, to render the query in the previous section you can get the data results in the following manner:

Copy
import { RichText } from 'prismic-reactjs'

// (...) Query definition

export default ({ data }) => {
  // Required check for no data being returned
  const doc = data.prismic.allHomepages.edges.slice(0,1).pop();
  if (!doc) return null;
  
  return (
    <div>
      <h1>{RichText.render(doc.node.title)}</h1>
      <h3>{RichText.render(doc.node.description)}</h3>
    </div>
  );
}

Take note how the data object follows the same structure of the defined query, where 'edges' is always an array of results and 'node' is every document that has been fetched. With this as a base, you can fetch any data field from your documents in your Prismic repository, and render them properly, including slices and data from linked documents.

When building a Gatsby web application, you should utilize the <Link> component to create links to internal documents. When handling links to Documents, you will need to fetch their _meta data field in order to build the Link component with the help of the linkResolver.

Copy
import { graphql, Link } from 'gatsby'
import { linkResolver } from '../utils/linkResolver'

export const query = graphql`
{
  prismic {
    allBlog_posts {
      edges {
        node {
          page_link {
            ... on PRISMIC_Page {
              _meta {
                uid
                lang
                type
              }
            }
          }
        }
      }
    }
  }
}
`

export default ({ data }) => {
  const doc = data.prismic.allBlog_posts.edges.slice(0,1).pop();
  if(!doc) return null;
  
  return(
    <div>
      <Link to={linkResolver(doc.node.page_link._meta)}
      Page Link
      </Link>
    </div>
  );
}


Building pages dynamically

In order to create pages from a template, you have to define in the plugin configuration the custom type these pages will be generated from, the path that these will be accessible through, the component used as a template to generate them and the placeholder path used for documents that don't exist yet. This last one is particularly necessary for previewing unpublished documents.

Copy
pages: [{ 
  type: 'Page',          // TypeName from prismic
  match: '/page/:uid',   // Pages will be generated under this pattern
  path: '/pages',        // Placeholder page for unpublished documents
  component: require.resolve('./src/templates/page.js'),
}]

type is the document custom type that will be used as the source for generating the dynamic pages. Make sure to capitalize the first letter, as well as using a repeatable type. Regular generation using the /pages folder is more adequate for singleton custom types.

match, as the name indicates, is a pattern that will be used to map the documents you have in your repository. You can use variables :uid and :lang to generate your paths in a way that fits the navigation planned for your site.

path is the placeholder that will be used when previewing documents that have not been published. For these drafts we have this separate path, where details will be passed as part of the url query. So a drafted page with uid 'not-published-yet' would be accessible under the path /pages?uid=not-published-yet

component is fairly straightforward, it defines the folder path where the template that will generate these pages resides.

In order to build a component that can be used as a template, it is necessary to define the query variables. Their value is shared with the 'match' field in the plugin configuration.

Here we have a small example of a template file ./src/templates/page.js

Copy
export const query = graphql`
query PageQuery($uid: String) {
  prismic {
    allPages(uid: $uid) {
      edges {
        node {
          title
          description
        }
      }
    }
  }
}
`

const Page = props => {
  const doc = props.data.prismic.allPages.edges.slice(0,1).pop();
  if(!doc) return null;
  
  return (
    <div>
      <h1>{RichText.render(doc.node.title)}</h1>
      <h3>{RichText.render(doc.node.description)}</h3>
    </div>
  );
}

export default Page;

Take note of the use of query variables to define a single document to fetch.


Previews

Enabling Prismic's Preview feature is simple. In gatsby-config.js fill out the related fields:

Copy
{ resolve: `gatsby-source-prismic-graphql`,
  options: {
    path: '/preview',
    previews: true,
    // ...
  }
}

The path field is the same link resolver endpoint you need to setup in your Prismic repository, which is by default '/preview'.

Reviewing the documentation, you will notice a repeating set of lines whenever data is fetched, the check for no data being returned. Be careful to include these lines if you wish to be able to preview documents that have not yet been published, since that document check allows for the redirection to the appropriate page.

Copy
const doc = data.prismic.allPages.edges.slice(0,1).pop();
if(!doc) return null;

Furthermore, the values for the placeholder 'path' field in gatsby-config.js should never be the same as files in the /pages folder, since this would create issues when mapping the required routes.

With these considerations in place, previews and drafts should be usable in your web application.


Using the HTML Serializer

It is a good idea to modify your web application so that links included as part of rich text elements are automatically handled as Gatsby's Link elements, specifically so for internal documents. This way your Gatsby web app will provide a consistent navigation experience for users. You can achieve this by modifying the HTML Serializer so that <a> tags are interpreted as <Link> when building pages.

Check the code below for an example that handles both regular links and links as part of an embedded image in a rich text field:

Copy
// -- The HTML Serializer
// This function will be used to modify the way that a Rich Text or Title field is rendered.

import React from 'react'
import { Link as PrismicLink, RichText } from 'prismic-reactjs'
import { linkResolver } from './linkResolver'
import { Link } from "gatsby"

const Elements =  RichText.Elements;

export default function (type, element, content, children, index) {
  // Generate links to Prismic Documents as <Link> components
  if (type === Elements.hyperlink) {
    let result = ''
    const url = PrismicLink.url(element.data, linkResolver)

    if (element.data.link_type === 'Document') {
      result = <Link to={ url } key={ index }>{ content }</Link>
    } else {
      const target = element.data.target ? { 'target':element.data.target, 'rel':'noopener' } : {}
      result = <a href={ url } { ...target } key={ index }>{ content }</a>
    }
    return result
  }

  // If the image is also a link to a Prismic Document, it will return a <Link> component
  if (type === Elements.image) {
    let result = <img src={ element.url } alt={ element.alt || '' } copyright={ element.copyright || '' } />

    if (element.linkTo) {
      const url = PrismicLink.url(element.linkTo, linkResolver)

      if (element.linkTo.link_type === 'Document') {
        result = <Link to={ url } key={ index }>{ result }</Link>
      } else {
        const target = element.linkTo.target ? { 'target':element.linkTo.target, 'rel':'noopener' } : {}
        result = <a href={ url } { ...target }>{ result }</a>
      }
    }
    const wrapperClassList = [element.label || '', 'block-img'];
    result = <p className={ wrapperClassList.join(' ') } key={ index }>{result}</p>
    return result
  }

  // Return null to stick with the default behavior for everything else
  return null;
};

You can use the HTML Serializer in Gatsby just as with any other framework, just pass it as part of the render function for a given rich text field.


Deploying your site

You can deploy your production-ready web application to any provider that supports static sites. Follow Gatsby's team's recommendations for deploying to platforms like Now, Netlify, GitHub Pages and more.

If you choose a provider that supports builds triggered by webhooks such as Netlify, you can configure Prismic's Webhooks to automatically rebuild your static pages whenever the content in your Prismic repository is updated. Follow Netlify's instructions to generate a webhook url, which will take the form of https://api.netlify.com/build_hooks/xxxxxxxxxxxxxxx. You can now add this to your repository's Webhooks. Now, whenever a content update triggers it, Netlify will automatically start a fresh build of your site.


A working example

We provide an example project using Gatsby to develop a web application for a fictional coffee shop. You can review the code for working examples of all the topics touched in this documentation:

https://github.com/prismicio/prismic-gatsby-coffee-sample

You can bootstrap this demo, installing the repository and filling out sample content as well, by using the command-line tool

Copy
$ npm install -g prismic-cli
$ prismic theme https://github.com/prismicio/prismic-gatsby-coffee-sample

You can test out the Previews functionality straight away with this sandbox project, as well as fully explore both the code and the content repository. And a deployed version of this example site is available here:

https://gatsby-coffee-demo.netlify.com/