Gatsby Cloud Docs

Creating an Extension or Widget to Preview Content

Table of Contents

Introduction

To deliver that first-class preview experience your users expect in a traditional product or CMS, your product must have a mechanism to extend and add a custom extension or widget. This utility will enable your users to preview content in your product in Gatsby’s Cloud offerings (Gatsby Preview, in this example).

Let’s take a look at how we can deliver this functionality.

Example

First, a concrete example is oftentimes extremely helpful. Your product may be a headless CMS and your users need the ability to preview content so that they know what the content they’re editing now will look like in the context of the deployed application. If the user is editing some resource that corresponds to a URL (for example: a “Page” content type), this functionality needs to open up the exact page being edited. As a helpful example, the functionality may look something like this:

Contentful Sidebar Extension

To deliver this experience, your product must necessarily have the following:

  1. The ability to add a custom sidebar extension or widget
  2. The ability to configure this extension with options (e.g. a webhook URL)

Let’s dive into what we provide to enable you to build out this widget.

Preview Instance

When using Gatsby Cloud (specifically Gatsby Preview), we provide the user a hot-reloading, preview-aware instance of their Gatsby application. This running application refreshes when content in your product updates or when new content is pushed to the Git repo. As such, a typical scenario may be something like:

  1. User creates, updates, or deletes content in your product
  2. The Preview environment is refreshed and the updated content is available
  3. The user now needs to know the path to the resource and to know when it’s available

3) is what you’ll be solving with this extension. Given a resource (e.g. a Page) that can be associated with a URL in the application, the preview extension needs to be able to provide a means to open the associated URL in Gatsby Preview.

We provide two helpful mechanisms for an extension to know when content is updated and detect what the URL is for the requested resource. Each are driven via the URL of the running application in Gatsby Preview.

  1. A webhook that returns a 200 when content has updated
  2. A GraphQL interface that can be queried to get a slug for a resource (or more broadly, any piece of data from the running application’s GraphQL schema)

Webhook

The extension will need to be aware of the running application’s URL (e.g. https://some-customer-1234.gtsb.io), so this will need to be configured in the extension. This URL drives everything mentioned above, in this case, let’s focus on the webhook.

The webhook is triggered via a POST request, and the general idea will look something like this:

const checkPreviewInstance = webhookURL => {
  return fetch(webhookURL, {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "x-preview-update-source": "your-product-extension",
    },
    body: JSON.stringify({}),
  })
    .then(() => {
      // do something on success, e.g. display a success/updated message
    })
    .catch(e => {
      // do something on error, e.g. display an error message
    })
}

This general idea can be used to inform the user of when content has been updated and is ready for viewing. Next, let’s associate a running application’s updated content with the current piece of content being edited in your product.

Querying Data with GraphQL

Every Gatsby application exposes a GraphQL API that can be queried from this extension. The API is available at {APP_URL}/___graphql e.g. https://some-customer-1234.gtsb.io/___graphql.

An example query could look like:

query GetAllRoutes {
  allSitePage {
    nodes {
      path
    }
  }
}

which will return a JSON structure like:

{
  "data": {
    "allSitePage": {
      "nodes": [
        {
          "path": "/"
        },
        {
          "path": "/404/"
        },
        {
          "path": "/contact/"
        }
      ]
    }
  }
}

Next, we need a way to associate some piece of content with one of these paths. This may not be necessary if your product stores a unique slug for each content type, but it is often necessary to handle cases in which slugs may be prefixed or may not directly map to the running application.

For example, let’s presume we have a Page content type that has a slug of learn-more-about-us. We could query the GraphQL schema like so to request the full path:

const getPathFromSlug = (slug, previewURL) => {
  return fetch(`${previewURL}/___graphql`, {
    method: `POST`,
    headers: {
      "content-type": "application/json",
    },
    body: JSON.stringify({
      query: `
        query GetPathFromSlug($slugExpr: String!) {
          sitePage( path: { regex: $slugExpr }) {
            path
          }
        }
      `,
      variables: {
        slugExpr: `/${slug}/`,
      },
    }),
  })
    .then(res => res.json())
    .then(({ data }) => {
      // do something with data, e.g.
      // window.open(`${previewURL}/${data.sitePath.page}`)
    })
}

At this point, we’ve now exposed the necessary functionality to associate a content type with a real page (by slug!) and we’ve provided a means to know when content is updated. With this new-found knowledge, you can now build out your extension using these approaches. More research will be required in building out your extension with the API you provide, but this serve as a guiding light for understanding the functionality Gatsby Cloud exposes to help make this as easy as possible.

Examples

Concrete examples are helpful to illustrate a concept. Let’s consider some examples.

Contentful

Consider checking out Contentful’s UI Extensions documentation as well as the Gatsby Preview extension.

DatoCMS

Check out DatoCMS Plugins particularly the Gatsby Cloud plugin.

Best practices for a great user experience

In order for your CMS users to benefit from a Gatsby Preview extension, the following usability goals should be met.

1. Users can see the extension above the fold when editing content.

Here’s an example from Contentful. The Gatsby Cloud extension is visible above the fold when editing content.

A screenshot of what the Gatsby Cloud extension looks like in a piece of content in Contentful

Why is this goal important?

The reason why it’s important to make the extension visible above the fold is because if users have to scroll down to see the extension, many of them will never notice it. And, since Gatsby Cloud’s preview extension provides functionality that content editors are accustomed to using, they expect to see it easily whenever they are editing content.

What can CMS vendors do to reach that usability goal?

CMS vendors can reach the usability goal by making sure this statement is true:

Extensions are placed in prime areas (generally the top-right part of the screen). Organizations should be able to choose where extensions are placed and ideally Gatsby Cloud can automatically position its extension there.