colin tinney



Creating an authenticated preview site for Gatsby and Contentful using Netlify Identity

April 21, 2019

Learn how to setup a preview instance of your Gatsby site using the Contentful Preview API and authenticate it using Netlify Identity with Google OAuth.


Contentful provides a Preview API that returns unpublished content (e.g. draft blog posts) to allow users to preview this content on their website.

Setting up a server to host an instance of your Gatsby website that uses the Preview API and is accessible within a local/corporate network is relatively trivial. In my case, I am working with a remote content creator; we do not work on the same network nor do I have a dedicated local network available for VPN access. Gatsby provides tutorials on authenticating routes but in this case I want the entire website to be authenticated without having to re-create page routes.

The solution I came up with is to run a preview instance on Netlify that uses Netlify Identity for authentication and Netlify Deploy Contexts to ensure that the builds on a target branch use the Preview API and turn on authentication. Contentful Webhooks are used to trigger re-deploys on Netlify to ensure the preview website stays updated.

Login screen

To summarize the changes:

  • Wrap Gatsby's root element in a higher-order component that uses Netlify Identity to check if the user is logged in

    • If the user isn't logged in, redirect to a login screen
  • Add support for environment variables to force builds to use the Contentful Preview API
  • Create a deploy context on Netlify for the target branch (e.g. develop or preview) that sets environment variables
  • Add a webhook to Contentful to trigger re-deploys for the target branch deploy context


This post will not cover how to set this up from scratch.

There are some prerequisites:

There are plenty of good resources online (1, 2) you can use to learn how to set this up.



We can create a wrapper service for netlify-identity-widget that our application can use to check if the user is authenticated and allow the user to login.

First, install it as a dependency via npm i -S netlify-identity-widget.


import netlifyIdentity from 'netlify-identity-widget';

// `window` won't be defined on SSR builds
if (typeof window !== 'undefined') {
  window.netlifyIdentity = netlifyIdentity;

export default {
  isAuthenticated: false,
  authenticate(callback) {
    this.isAuthenticated = true;;
    netlifyIdentity.on('login', user => callback(user));
  currentUser() {
    return netlifyIdentity.currentUser();

Next, we create a higher-order component that we can use to wrap page components.


import React, { PureComponent } from 'react';

import auth from './auth';

// We can check this before the component is rendered.
const alreadyLoggedIn = auth.isAuthenticated
|| auth.currentUser() !== null;

export default function withAuth(PageComponent) {
  return class WithAuthPageComponent extends PureComponent {
    state = {
      isLoggedIn: false,
      redirectPath: null,

    componentDidMount() {
      // When it's first mounted, store the path name.
      // This allows us to redirect to it after the user has logged in.
      const path = window.location.pathname;
        redirectPath: !alreadyLoggedIn && path !== '/'
          ? path
          : null,

    login = () => auth.authenticate((user) => {
      if (user) {
          isLoggedIn: true,

    render() {
      const {
      } = this.state;

      if (!isLoggedIn && !alreadyLoggedIn) {
        return (
            // This will center the button vertically
            // and horizontally. You don't need to do this necessarily.
              left: '50%',
              top: '50%',
              transform: 'translate(-50%, -50%)',

      if (redirectPath) {
        return null;

      return (
        <PageComponent {...this.props} />

Now, we can create the function to do the wrapping (based on an environment variable -- we'll cover that after).


import React from 'react';
import PropTypes from 'prop-types';

import withAuth from './withAuth';

const useAuth = process.env.ENABLE_NETLIFY_AUTH === 'true';

const wrapRootElement = ({
}) => {
  const RootElement = () => (

  if (!useAuth) {
    return <RootElement />;

  const ElementWithAuth = withAuth(RootElement);
  return <ElementWithAuth />;

wrapRootElement.propTypes = {
  element: PropTypes.node.isRequired,

export default wrapRootElement;

Now, we need to do the element wrapping via Gatsby's Browser APIs and Node APIs. We can use wrapRootElement specifically.


import wrapRootElementWithAuth from './src/auth/wrapRootElementWithAuth';

export const wrapRootElement = wrapRootElementWithAuth;


import wrapRootElementWithAuth from './src/auth/wrapRootElementWithAuth';

export const wrapRootElement = wrapRootElementWithAuth;

Now, we need to add support for ENABLE_NETLIFY_AUTH. Since we're using it on the client-side, we need to make it accessible to modules. This can be accomplished with gatsby-plugin-env-variables.

After installing it as a dependency (npm i -S gatsby-plugin-env-variables), add the following lines to your gatsby-config.js file:

plugins: [{
  resolve: 'gatsby-plugin-env-variables',
  options: {
    // Variables in the whitelist will be available to builds as process.env.<NAME>, just
    // like Node processes
    whitelist: [

Now, we must add environment variable support to switch to the Contentful Preview API.

Let's name this variable CONTENTFUL_USE_PREVIEW.

With gatsby-source-contentful, there is a host option we can use. I moved plugin options to their own module for encapsulation.

NOTE: You will need both Preview and Delivery API tokens setup in your build environment.


// We can use dotenv to read environment variables from a file (e.g. local environment).
// This is not required if you don't want to preview locally.
// This does require you to install it as a dependency:
//  `npm i -S dotenv`
  path: '.env',

const {
} = process.env;
const usePreview = CONTENTFUL_USE_PREVIEW === 'true';

const host = usePreview
  ? ''
  // If `null`, defaults to the Delivery API 
  : null;
const accessToken = usePreview
  // The Preview API uses a different token than the Delivery API

if (!spaceId || !accessToken) {
  throw new Error(
    'Contentful spaceId and access token need to be provided',

module.exports = {

Then, pass them to the plugin as options:


const contentfulOptions = require('./contentful-options');

plugins: [{
  resolve: 'gatsby-source-contentful',
  options: contentfulOptions


Now, we need to setup Netlify to support authenticated builds using the Preview API.

  1. Add the following environment variables:

    • CONTENTFUL_PREVIEW_TOKEN - Contentful API token
  2. Add a deploy context (Site settings > Build & deploy > Deploy Contexts) for the target branch, e.g. develop

    Deploy Contenxt

  3. Update your build settings via netlify.toml to use authentication and the preview API


    # Settings for `develop` branch deploys.
    # If your target branch is named something else,
    # the context will be "context.<NAME>.environment".
      ENABLE_NETLIFY_AUTH = "true"
  4. Enable Identity, add Google as a provider, and set it to invite-only

  5. Finally, add a build hook for the develop deploy context and copy it to your clipboard

    Build hook


Finally, we need to setup Contentful to trigger re-deploys of develop when content changes.

Open the Webhooks for your environment and add the copied build hook.


We can add linking from content to the preview website:

  1. Open Content Preview for your environment

  2. Select the content you want

  3. Use the full Netlify URL for the preview URLs

    • e.g.{entry.fields.slug}

    Content Preview URLs

Then, a preview button will show up for creators on the right-side pane when creating content such as blog posts.

Preview button


To test the setup, push the changes to the develop branch after configuring Contentful and Netlify. Upon a successful deploy, you should be redirected to the login page when accessing the website.


Questions? E-mail me.

up ↑