Making CMS for static content with Sanity and Gatsby

As many of you know, we love gatsby a lot at truesight and we are using it for our main site as well. Most of the content on the site is pretty static and we have to go back to code to edit anything. say hero title, hero description, client images, etc. Wouldnt be awesome if we could edit everything without touching code? Well, my friend, you are about to see something awesome now 😎

Enter Sanity.io

Sanity.io is a headless CMS that content editors can use to edit and publish content. So in this post, I'll share how to setup sanity and use it in gatsby project.

Install the Sanity CLI

We will require to install sanity CLI to set up the sanity project. Here is the simple command to install it:

npm install --global @sanity/cli

Create a sanity project

sanity init

This command will ask to log in sanity, create a project, or use an existing one, create a dataset, and select a project template.

Run the studio

To start the development server for Sanity Studio, run this command in your sanity project folder:

sanity start

This builds the initial JavaScript code required to run the studio and starts a local development web server. As you modify and save the code, the server will automatically rebuild the studio and refresh the browser.

The studio is just an HTML file and some JavaScript bundles that run in your browser. It talks to the Sanity API which stores your data and lets you query it from whatever platform or front-end you want.

Now let's create a schema for our site.

Creating Schema

Schemas are basically blueprints of your content. They define the fields we will for the dashboard. For this tutorial, we will create a two custom schema called siteSettings which will set the basic settings of the site like Hero Title, Hero Description.

Now at schemas/schema.js, you will see:

import createSchema from "part:@sanity/base/schema-creator"
import schemaTypes from "all:part:@sanity/base/schema-type"

export default createSchema({
  name: "default",
  types: schemaTypes.concat([
    /* Your types here! */
  ]),
})

Now let's add siteSettings schema to it. First, create siteSettings.js

// /schemas/documents/siteSettings
export default {
  name: "siteSettings",
  type: "document",
  title: "Site Settings",
  __experimental_actions: ["update", /* 'create', 'delete', */ "publish"],
  fields: [
    {
      name: "heroTitle",
      type: "string",
      title: "Hero Title",
    },
    {
      name: "heroDescription",
      type: "string",
      title: "Hero Description",
    },
  ],
}

Add the siteSettings to the list of our schemas.

// /schemas/schema.js
import createSchema from "part:@sanity/base/schema-creator"

import schemaTypes from "all:part:@sanity/base/schema-type"
import siteSettings from "./documents/siteSettings"

export default createSchema({
  name: "default",
  types: schemaTypes.concat([
    siteSettings,  ]),
})

Now if you load sanity studio in our browser at localhost:333/desk, you will see our siteSetting document there. But there seems to be a problem with it, we can't create any document from it. It's because of the __experimental_actions in schema file. We only want it to have single document of siteSettings. But to make it work, we will need to make some changes to our sanity dashboard.

Singletons and "one-off" documents

Documents type which can only have a single document are called 'one-off' documents in sanity. We will need to tell this to sanity that siteSettings is one-off document. To do so, we will need to modify sanity desk structure ( Sanity Dashboard is called Desk ). Let's do it.

Create a file called deskStructure.js at root and paste following code in it:

// deskStructure.js
import { GoSettings } from "react-icons/lib/go"
import S from "@sanity/desk-tool/structure-builder"

const hiddenDocTypes = listItem => !["siteSettings"].includes(listItem.getId())

export default () =>
  S.list()
    .title("Content")
    .items([
      S.documentListItem()
        .schemaType("siteSettings")
        .title("Site settings")
        .icon(GoSettings)
        .child(
          S.document()
            .schemaType("siteSettings")
            .documentId("siteSettings")
            .views([S.view.form()])
        ),
      ...S.documentTypeListItems().filter(hiddenDocTypes),
    ])

Now, in your sanity.json file, pass the file path to tell sanity we will be modifying desk structure.

// sanity.json
{
  ...
  "parts": [
    {
      "name": "part:@sanity/base/schema",
      "path": "./schemas/schema"
    },
    {      "name": "part:@sanity/desk-tool/structure",      "path": "./deskStructure"    }  ]
}

Now restart your server and you should be seeing something like this:

Explanation:

  1. Basically first we created a file called deskStructure which telling sanity to load only one document of document type siteSettings.
  2. After that, we passed this file to sanity config in sanity.json

That's all we needed to do on the sanity side. Let's load this data in gatsby.

Connecting Gatsby + Sanity

Lucky for us, there exists a gatsby plugin (https://github.com/sanity-io/gatsby-source-sanity) to connect gatsby to sanity.

Let's add the this plugin

yarn add gatsby-source-sanity

Before adding this plugin to gatsby, make sure you create a read token from sanity ( https://manage.sanity.io/ ) for your project.

After you have created the token, in your gatsby-config.js, add the following code:

module.exports = {
  // ...
  plugins: [
    {
      resolve: `gatsby-source-sanity`,
      options: {
        projectId: `<your-project-id>`,
        dataset: `<your-dataset-name>`,
        token: `<your-sanity-read-token>`, // you can also use it from env variable
      },
    },
    // ...
  ],
  // ...
}

Make sure to replace the variables with correct values and restart the server.

Now let's call our sanity data in gatsby. In our pages/index.js file, add the following graphql query.

import React from "react"
import { graphql } from "gatsby"

export const query = graphql`
  query FrontpageQuery {
    site: sanitySiteSettings(_id: { regex: "/(drafts.|)siteSettings/" }) {
      heroTitle
      heroDescription
    }
  }
`

const IndexPage = props => {
  const { data, errors } = props

  if (errors) {
    // you can handle errors here.
  }

  const heroTitle = site.heroTitle
  const heroDescription = site.heroDescription

  return (
    <div>
      <h1>{heroTitle}</h1>
      <h3>{heroDescription}</h3>
    </div>
  )
}

export default IndexPage

Explaination

  1. Since we have connected gatsby to sanity with gatsby-source-sanity plugin, we have access to every data in sanity data. To fetch data from sanity, we can simple add graphql query now in our pages like we have done with FrontpageQuery.
  2. After adding query, the sanity data is available in our component and we are free to use anyway we want

That's it for this tutorial, we will be adding more tutorial sanity very soon 😃 Stay tuned.