Skip to main content

Overview

This integration guide shows you how to how to update your existing project to:
  1. install the ButterCMS package
  2. instantiate ButterCMS
  3. create components to fetch and display each of the three ButterCMS content types: Pages, Collections, and Blog Posts.
In order for the snippets to work, you’ll need to setup your dashboard content schemas inside of ButterCMS first.

Starter project

Or, you can jump directly to the starter project below, which will allow you to clone, install, run, and deploy a fully working starter project that’s integrated with content already inside of your ButterCMS account.

Gatsby Starter Project

Hit the ground running with a pre-configured Gatsby + ButterCMS setup.

Installation

npm install buttercms gatsby-source-buttercms
Add your API token to .env:
BUTTERCMS_API_TOKEN=your_api_token

Configuration

Add the source plugin to your Gatsby config:
// gatsby-config.js
require('dotenv').config();

module.exports = {
  plugins: [
    {
      resolve: 'gatsby-source-buttercms',
      options: {
        authToken: process.env.BUTTERCMS_API_TOKEN,
        contentFields: {
          keys: ['navigation', 'footer', 'brands'],
        },
        pageTypes: ['landing-page'],
        locales: ['en'],
      },
    },
  ],
};
For complete SDK documentation including all available methods and configuration options, see the JavaScript SDK Reference.

Pages

Create pages dynamically in gatsby-node.js:
// gatsby-node.js
exports.createPages = async ({ actions, graphql }) => {
  const { createPage } = actions;

  const { data } = await graphql(`
    {
      allButterPage(filter: { page_type: { eq: "landing-page" } }) {
        nodes {
          slug
        }
      }
    }
  `);

  data.allButterPage.nodes.forEach((page) => {
    createPage({
      path: page.slug === 'home' ? '/' : `/${page.slug}`,
      component: require.resolve('./src/templates/landing-page.js'),
      context: { slug: page.slug },
    });
  });
};
// src/templates/landing-page.js
import React from 'react';
import { graphql } from 'gatsby';

export default function LandingPage({ data }) {
  const page = data.butterPage;

  return (
    <main>
      <h1>{page.headline}</h1>
      <p>{page.subheadline}</p>
      {page.hero_image && (
        <img src={page.hero_image} alt={page.headline} />
      )}
      <div dangerouslySetInnerHTML={{ __html: page.body }} />
    </main>
  );
}

export const query = graphql`
  query($slug: String!) {
    butterPage(slug: { eq: $slug }, page_type: { eq: "landing-page" }) {
      slug
      headline
      subheadline
      hero_image
      body
    }
  }
`;

Collections

// src/pages/brands.js
import React from 'react';
import { graphql } from 'gatsby';

export default function BrandsPage({ data }) {
  const brands = data.butterCollection.value;

  return (
    <main>
      <h1>Our Brands</h1>
      <ul>
        {brands.map((brand, index) => (
          <li key={index}>
            <img src={brand.logo} alt={brand.name} />
            <h2>{brand.name}</h2>
            <div dangerouslySetInnerHTML={{ __html: brand.description }} />
          </li>
        ))}
      </ul>
    </main>
  );
}

export const query = graphql`
  {
    butterCollection(key: { eq: "brands" }) {
      value {
        name
        logo
        description
      }
    }
  }
`;

Dynamic components

Component Renderer

// src/components/ComponentRenderer.js
import React from 'react';
import Hero from './Hero';
import Features from './Features';
import Testimonials from './Testimonials';
import CTA from './CTA';

const componentMap = {
  hero: Hero,
  features: Features,
  testimonials: Testimonials,
  cta: CTA,
};

export default function ComponentRenderer({ components }) {
  return (
    <>
      {components.map((component, index) => {
        const Component = componentMap[component.type];
        if (!Component) return null;
        return <Component key={index} {...component.fields} />;
      })}
    </>
  );
}

Example Component

// src/components/Hero.js
import React from 'react';

export default function Hero({ headline, subheadline, image, button_label, button_url }) {
  return (
    <section className="hero">
      <h1>{headline}</h1>
      <p>{subheadline}</p>
      {button_label && <a href={button_url} className="btn">{button_label}</a>}
      {image && <img src={image} alt={headline} />}
    </section>
  );
}

Using in templates

// src/templates/component-page.js
import React from 'react';
import { graphql } from 'gatsby';
import ComponentRenderer from '../components/ComponentRenderer';

export default function ComponentPage({ data }) {
  const page = data.butterPage;

  return (
    <main>
      <ComponentRenderer components={page.body} />
    </main>
  );
}

export const query = graphql`
  query($slug: String!) {
    butterPage(slug: { eq: $slug }, page_type: { eq: "landing-page" }) {
      slug
      body {
        type
        fields {
          headline
          subheadline
          image
          button_label
          button_url
        }
      }
    }
  }
`;

Blog

Add to gatsby-node.js:
// gatsby-node.js (add to createPages)
createPage({
  path: '/blog',
  component: require.resolve('./src/templates/blog-list.js'),
  context: {},
});
// src/templates/blog-list.js
import React from 'react';
import { graphql, Link } from 'gatsby';

export default function BlogList({ data }) {
  const posts = data.allButterPost.nodes;

  return (
    <main>
      <h1>Blog</h1>
      <ul>
        {posts.map((post) => (
          <li key={post.slug}>
            <h2><Link to={`/blog/${post.slug}`}>{post.title}</Link></h2>
            <p dangerouslySetInnerHTML={{ __html: post.summary }} />
            <span>By {post.author.first_name} {post.author.last_name}</span>
          </li>
        ))}
      </ul>
    </main>
  );
}

export const query = graphql`
  {
    allButterPost(sort: { published: DESC }) {
      nodes {
        slug
        title
        summary
        published
        author {
          first_name
          last_name
        }
      }
    }
  }
`;

Preview Mode

Enable content editors to preview draft content before publishing:
// gatsby-config.js
module.exports = {
  plugins: [
    {
      resolve: 'gatsby-source-buttercms',
      options: {
        authToken: process.env.BUTTERCMS_API_TOKEN,
        preview: process.env.GATSBY_PREVIEW === 'true',
        // ...other options
      },
    },
  ],
};
For Gatsby Cloud or other preview hosts, set GATSBY_PREVIEW=true in the preview environment.

SEO

// src/templates/landing-page.js
import React from 'react';
import { graphql } from 'gatsby';

export default function LandingPage({ data }) {
  const page = data.butterPage;
  const seo = page.seo || {};

  return (
    <>
      <title>{seo.title || page.headline}</title>
      <meta name="description" content={seo.description} />
      <meta property="og:title" content={seo.og_title || seo.title || page.headline} />
      <meta property="og:description" content={seo.og_description || seo.description} />
      {seo.og_image && <meta property="og:image" content={seo.og_image} />}

      <main>
        <h1>{page.headline}</h1>
        <div dangerouslySetInnerHTML={{ __html: page.body }} />
      </main>
    </>
  );
}

export const query = graphql`
  query($slug: String!) {
    butterPage(slug: { eq: $slug }, page_type: { eq: "landing-page" }) {
      slug
      headline
      body
      seo {
        title
        description
        og_title
        og_description
        og_image
      }
    }
  }
`;

Resources

Gatsby Starter

Pre-configured starter project

JavaScript SDK

Complete SDK reference

GitHub Repository

View source code

Content API

REST API documentation