JSON in Gatsby


Editing JSON in Gatsby

Creating forms for content provided by the gatsby-transformer-json plugin is made possible by two plugins:

  • gatsby-tinacms-json: Provides hooks and components for creating JSON forms.
  • gatsby-tinacms-git: Extends the gatsby dev server to write changes to the local filesystem; and registers a CMS API for saving changes to that backend.

Note on top-level arrays:

With Gatsby, due to the way gatsby-transformer-json handles JSON, Tina cannot accept a top-level array in the JSON file. For example:

[{ "breakfast": "granola" }, { "lunch": "tacos" }, { "dinner": "pizza" }]

If you need a top-level array of objects like above, we recommend creating separate JSON files for each object. If you want to keep the data in one file, we recommend creating a single top-level object with the array nested as a value. For example:

{
  "menu": [
    { "breakfast": "granola" },
    { "lunch": "tacos" },
    { "dinner": "pizza" }
  ]
}

If you adjust your data structure, it may affect the way you query for the file in Gatsby. Read more on the specifics of how JSON files are transformed to nodes in GraphQL.

Installation

    npm install --save gatsby-source-filesystem gatsby-transformer-json gatsby-tinacms-git gatsby-tinacms-json

or

    yarn add gatsby-source-filesystem gatsby-transformer-json gatsby-tinacms-git gatsby-tinacms-json

Configuring Gatsby

gastby-config.js

module.exports = {
  plugins: [
    // ...
    'gatsby-tinacms-json',
    {
      resolve: `gatsby-source-filesystem`,
      options: {
        path: `${__dirname}/src/data`,
        name: 'data',
      },
    },
    'gatsby-transformer-json',
  ],
}

This will create a node for each json file in the src/data directory. You can then query that data like so:

query MyQuery {
  dataJson(firstName: { eq: "Nolan" }) {
    lastName
    firstName
  }
}

Creating JSON Forms

There are two approaches to registering JSON forms with Tina. The approach you choose depends on whether the React template is a class or function.

  1. useJsonForm: A Hook used when the template is a function.
  2. JsonForm: A Render Props component to use when the template is a class component.

Note: required query data

In order for the JSON forms to work, you must include the following fields in your dataJson graphql query:

  • rawJson
  • fileRelativePath

An example dataQuery in your template might look like this:

query DataQuery($slug: String!) {
  dataJson(fields: { slug: { eq: $slug } }) {
    id
    firstName
    lastName

    rawJson
    fileRelativePath
  }
}

Additionally, any fields that are not queried will be deleted when saving content via the CMS.

useJsonForm

This is a React Hook for creating Json Forms. This is the recommended approach if your template is a Function Component.

In order to use a form you must register it with the CMS. There are two main approaches to register forms in Tina: page forms and screen plugins. Please refer to the form concepts doc to get clarity on the differences.

Interface

useJsonForm(data): [values, form]

Arguments

  • data: The data returned from a Gatsby dataJson query.

Return

  • [values, form]
    • values: The current values to be displayed. This has the same shape as the data argument.
    • form: A reference to the CMS Form object. The form is rarely needed in the template.

Example 1: Page Forms

src/templates/blog-post.js

import { usePlugin } from 'tinacms'
import { useJsonForm } from 'gatsby-tinacms-json'

function BlogPostTemplate(props) {
  const [data, form] = useJsonForm(props.data.dataJson)

  usePlugin(form)

  return <h1>{data.firstName}</h1>
}

Example 2: Forms as Screens

Screens are additional UI modals accessible from the CMS menu. The useFormScreenPlugin let's us create and register new Screen Plugin based on a form. This is a great place to put forms for content that doesn't belong on any particular page.

src/components/layout.js

import { useFormScreenPlugin } from 'tinacms'
import { useJsonForm } from 'gatsby-tinacms-json'

function Layout(props) {
  const [data, form] = useJsonForm(props.data.dataJson)

  useFormScreenPlugin(form)

  return <h1>{data.firstName}</h1>
}

JsonForm

JsonForm is a Render Props based component for accessing CMS Forms.

This Component is a thin wrapper of useJsonForm and usePlugin. Since React Hooks are only available within Function Components you will need to use JsonForm if your template is Class Component.

Props

  • data: The data returned from a Gatsby dataJson query.
  • render({ data, form }): JSX.Element: A function that returns JSX elements
    • data: The current values to be displayed. This has the same shape as the data in the Json prop.
    • form: A reference to the CMS Form object. The form is rarely needed in the template.

src/templates/blog-post.js

import { JsonForm } from 'gatsby-tinacms-json'

class DataTemplate extends React.Component {
  render() {
    return (
      <JsonForm
        data={this.props.data.dataJson}
        render={({ data }) => {
          return <h1>{data.firstName}</h1>
        }}
      />
    )
  }
}

Customizing Json Forms

When using a json form with Tina, the shape of the data will initially be created with simple default text components. However, you may want to use Tina's more advanced components or specify things like labels etc. for each field.

Why customize the form?

  1. The default label for a field is it’s name.
  2. Every field is made a text component.
  3. The order of fields might not be consistent.

How to customize the form

The useJsonForm hook accepts an optional config object for overriding the default configuration. The following properties are accepted:

  • label: An optional label for the file
  • actions: A list of form actions, such as DeleteAction
  • fields: A list of field definitions
    • name: The path to some value in the data being edited. (e.g. rawJson.title)
    • component: The name of the React component that should be used to edit this field. The default options are: "text", "textarea", "color".
    • label: A human readable label for the field.
    • description: An optional description that expands on the purpose of the field or prompts a specific action.

Note: there may be additional properties specific to each field, but the above are the rudimentary properties of every field. Check the Fields section of the docs for particulars on the properties for each field.

import { usePlugin } from 'tinacms'
import { useJsonForm } from 'gatsby-tinacms-json'

function Page(props) {
  const [page, form] = useJsonForm(props.data.page, FormOptions)

  usePlugin(form)

  return (
    <section>
      <Wrapper>
        <h2>{page.hero_copy}</h2>
        <p>{page.supporting_copy}</p>
      </Wrapper>
    </section>
  )
}

const FormOptions = {
  fields: [
    {
      label: 'Hero Copy',
      name: 'rawJson.hero_copy',
      description: 'Hero copy for the main block',
      component: 'text',
    },
    {
      label: 'Supporting Copy',
      name: 'rawJson.supporting_copy',
      description: 'Choose your supporting copy for the hero',
      component: 'textarea',
    },
  ],
}

export default Page

Important: You may need to implement default values or dummy files to avoid a GraphQL error when a field is empty. Take a look at our empty field troubleshooting guide for more information.