Cover
TinaCMS

TinaCMS: probably the best way to edit your Gatsby site in 2020

Introduction
TinaCMS: probably the best way to edit your Gatsby site in 2020

A colleague recently pointed me on TinaCMS and I was interested immediately. I was looking for a solution to develop my own theme with and I already decided GatsbyJS would be the way to go. TinaCMS seemed like a perfect match.

Build real-time editing into your site. | TinaCMS
Tina is an open-source site editing toolkit for React-based frameworks — Gatsby & Next.js.

It claims itself as not being a CMS, it's more like a framework to edit content. The core of it is quite tiny. To make it useful you need to install further packages, e.g. to sync changes with Git or to save changes to certain formats like Markdown or JSON.

First impression

I've already set up a small project as a Proof of Concept and I must say I do really love the concept. However I think it's still very early in development and things are changing, errors are throwing here and there and you should probably not use it for critical projects that demand a high level of stability. At the moment! I highly recommend you to stay updated on this CMS as it seems very promising to me. I've even contributed to the project myself, just to fix a small bug.

Comparing to conventional CMSs

Tina does not provide data, it just provides a way to edit those. At the time of writing this, it's mainly possible to edit JSON and Markdown but I guess there is more to come. The neat thing of this concept is, that you can use TinaCMS on top of GatsbyJS without making it a fixed requirement. You can set up Tina to edit the files you are already using with Gatsby, but you don't have to match everything to the architecture of your CMS. If you use a CMS like DatoCMS or Contentful, you access an API that serves data and this data is served in a specific format with a specific structure. The more you depend on this structure the harder it gets to migrate your project to another system. In TinaCMS you will only define forms and fields that provide a mask to edit your content, but you could also remove Tina and your project will still work the same.

Install TinaCMS to your Gatsby project

Basically you will just need these two packages:

  • tinacms
  • gatsby-plugin-tinacms

To sync your changes with Git, what's probably the case with most Gatsby projects, you need to install gatsby-tinacms-git as well.

Depending on your structure you will then have to install:

  • gatsby-tinacms-json if you want to edit JSON files
  • gatsby-tinacms-remark if you want to edit Markdown files

After that you just need to include these in your gatsby-config.js :

    {
      resolve: 'gatsby-plugin-tinacms',
      options: {
        sidebar: {
          hidden: process.env.NODE_ENV === "production",
        },
        plugins: [
          "gatsby-tinacms-git",
          "gatsby-tinacms-remark",
          "gatsby-tinacms-json",
        ],
      },
    },

Setting up your forms with React

TinaCMS is split into two concepts of forms:

  • global: these are accessible in your whole app. Global forms are meant for things like site config, menus and everything that doesn't belong to a specific entity
  • local: a local form will only be displayed if they are rendered. So, if you use React, this means this form is displayed when the component that contains the form is rendered, so probably only on a specific route to add page specific forms

The adapter packages gatsby-tinacms-json and gatsby-tinacms-remark expose these as React hooks:

  • useGlobalJsonForm and useLocalJsonForm  for JSON
  • useGlobalRemarkForm and useLocalRemarkForm for Markdown

For example if have a Site component that should provide some global forms. So first I import the right hook and then I setup the form in my component. To get the existing data you will also have to query this via useStaticQuery.

import { useStaticQuery, graphql } from "gatsby"
import { useGlobalJsonForm } from "gatsby-tinacms-json"

const Site = ({ children }) => {
  const data = useStaticQuery(graphql`
    query SiteQuery {
      site: dataJson(
        fileRelativePath: { eq: "/content/data/site.json" }
      ) {
        title
        description
        logo {
          childImageSharp {
            fixed(width:200) {
              src
            }
          }
        }

        rawJson
        fileRelativePath
      }
    }
  `)

  // SiteForm will be defined in the next section
  const [site] = useGlobalJsonForm(data.site, SiteForm)

  return (
    <div>
      {typeof children === 'function' ? children({ site }) : children}
    </div>
  )
}

In this case I am just querying with Gatsby's useStaticQuery for the title, description and the logo of my site and I am then passing it to the useGlobalJsonForm hook, because this hook needs to know what data is already saved. As second argument I am passing the SiteForm which I define later.

Adding fields to a form

Next I need to define the fields I need to edit the data of the site. This is the second argument we are passing to the useGlobalJsonForm hook.

const SiteForm = {
  label: "Site",
  fields: [
    {
      label: "Title",
      name: "rawJson.title",
      component: "text",
      parse(value) {
        return value || ""
      },
    },
    {
      label: "Description",
      name: "rawJson.description",
      component: "text",
      parse(value) {
        return value || ""
      },
    },
    {
      label: 'Logo',
      name: 'rawJson.logo',
      component: 'image',

      previewSrc: (formValues, fieldProps) => {
        const pathName = fieldProps.input.name.replace("rawJson", "jsonNode")
        const imageNode = get(formValues, pathName)
        console.log(imageNode);
        if (!imageNode) return ""
        if (!imageNode.childImageSharp) return imageNode;
        return imageNode.childImageSharp.fixed.src
      },

      uploadDir: () => {
        return '/content/assets/'
      },

      parse: filename => `../assets/${filename}`,
    },
  ],
}

Here we simply create a form called Site which contains a title, description, and a logo. For the individual fields I recommend to just checkout the docs of TinaCMS.

Hint: If you are creating a JSON form, make sure to prepend the rawJson to your name. If find it a little misleading that this property is called name, while it might make sense for markdown, when using JSON it's basically the path in your JSON object. At the moment if you don't prepend rawJson you just won't find the data and it will not throw any error.

Accessing data

To access your data you can still use the principles you already know from Gatsby, nothing will change when you use TinaCMS. So the useStaticQuery or graphql export will still work. I am not going into detail on this one.

Inline Editing

TinaCMS also provides a way to edit your data inline, however I have not yet discovered this feature, as I am not a big fan of this. By experience I know that inline editing can always lead to trouble and doesn't add a lot of value.

Summary

I cannot pronounce how much I love the concept behind TinaCMS and I will keep exploring it and using it. Actually my current plan is to migrate Baretheme to be used with TinaCMS because it seems like it covers all my needs. Even though it's still not released as a stable version, you can still edit all the files without it if you experience any problems. Go ahead, give it a try and check it out!

Marc Mintel
Author

Marc Mintel

Marc Mintel is a self taught JavaScript and Frontend Developer with heavy focus on React and Vue.

View Comments
Next Post

Most weird bug (or security feature?) with iFrames and native drag and drop

Previous Post

Seven examples to test your React components with Jest and @testing-library

Success! Your membership now is active.