Preview Content Changes

Package versions

These docs use the following package versions:

  • prismic-reactjs: v1.3
  • @prismicio/client: v5

The docs will soon be updated to use the latest versions of these packages. If you wish to use the latest versions today, you can update your project by following the migration guides for each package.

This article will show you how to preview content changes without publishing your document or rebuilding your project.


Previews

One of the most powerful features of Prismic is Previews, which gives content editors the ability to preview content without publishing it to a live site. The Toolbar also allows you to edit your pages. On your website, click the edit button displayed on the bottom left page, select the part of the page that you want to edit, and you'll be redirected to the appropriate page in the Writing Room.

Update Next.js

For the following implementation of previews to work, you will need at least V10 of Next.js: run npm i next@latest to update.


1. Add the toolbar script

The toolbar script allows you to get the draft preview content from your Prismic repository when a change occurs and will allow you to clear the Prismic preview cookie when you are finished.

Add the toolbar script for your repository to the _document.js file in your project directory at ~/pages/_document.js. In this file, import the repository name, which should look like your-repo-name, and pass it to the toolbar script in the <head> component as a variable called repoName.

Set up a configuration file

Read the Next.js set-up guide to learn about creating a prismicConfiguration.js file.

The other option is to declare it directly in the file: export const repoName = 'your-repo-name'

You can see the full code from the _document.js below:

Copy
import Document, { Html, Head, Main, NextScript } from 'next/document'
import { repoName } from '../prismicConfiguration' // import from wherever this is set

class MyDocument extends Document {
  static async getInitialProps(ctx) {
    const initialProps = await Document.getInitialProps(ctx)
    return { ...initialProps }
  }

  render() {
    return (
      <Html>
        <Head>
          <meta charSet="utf-8" />
          <link
            href="https://fonts.googleapis.com/css?family=Lato:300,400,700,900"
            rel="stylesheet"
          />
          <link rel="icon" href="/favicon.png" type="image/png" />
          <script async defer src={`//static.cdn.prismic.io/prismic.js?repo=${repoName}&new=true`} />
        </Head>
        <body>
          <Main />
          <NextScript />
        </div>
      </Html>
    )
  }
}

export default MyDocument

2. Configure Next.js API preview routes

You need to set the Next Preview mode configuration. If it doesn't already exist, create an API directory: ~/pages/api. Inside of this ~/pages/api directory, create two new files:

  1. preview.js
  2. exit-preview.js (Required file to exit a preview session correctly)

2.1. preview.js

The preview configuration uses the Next.js setPreviewData method which sets some cookies on the browser, turns on the preview mode, and redirects the request to the appropriate URL. It also imports the Link Resolver and Client function, read the Next.js set-up guide to learn how to set these up.

Add the following code in the api/preview.js file:

Copy
import { linkResolver } from '../prismicConfiguration' // import from wherever this is set
import { Client } from '../utils/prismicHelpers'  // import from wherever this is set

export default async (req, res) => {
  const { token: ref, documentId } = req.query;
  const redirectUrl = await Client(req)
    .getPreviewResolver(ref, documentId)
    .resolve(linkResolver, '/');

  if (!redirectUrl) {
    return res.status(401).json({ message: 'Invalid token' });
  }

  res.setPreviewData({ ref });

  res.write(
    `<!DOCTYPE html><html><head><meta http-equiv="Refresh" content="0; url=${redirectUrl}" />
    <script>window.location.href = '${redirectUrl}'</script>
    </head>`
  );
  res.end();
};

The Link Resolver Function

This requires the use of a Link Resolver function so that the preview endpoint knows where to redirect to. You can learn more about Link Resolving by checking out the Link Resolver documentation.

You either need to define the Link Resolver in the Preview component or import it.

2.2. exit-preview.js

To exit the active Next.js preview session, you must add the /api/exit-preview.js file. When this route is hit the Next.js cookie is cleared and preview mode is switched off. Below is how this will look in different environments.

You can see the code for this file below:

Copy
const url = require('url')

export default async function exit(req, res) {
  // Exit the current user from "Preview Mode". This function accepts no args.
  res.clearPreviewData()

  const queryObject = url.parse(req.url, true).query
  const redirectUrl = queryObject && queryObject.currentUrl ? queryObject.currentUrl : '/'

  res.writeHead(307, { Location: redirectUrl })
  res.end()
}

3. Next.js preview helpers

Because of the unique way previews are handled in Next.js, you will need to add some extra code to have a full suite of functionality in your application.

3.1. useUpdatePreviewRef.js hook

To have hot reloads for preview data in your Next.js application, that's to say if a change is made and saved in Prismic your Next.js app will get the new data without the preview button needing to be clicked again, then you will need a helper function that uses the useEffect hook.

Practically what you need is to create a hook by making a file called useUpdatePreviewRef.js that checks the Prismic Preview cookie against the _next_preview_data cookie and triggers a new Next.js preview session if it doesn't match. Add the useUpdatePreviewRef.js in the utils folder.

You need to install the js-cookie dependency for this to work.

  • npm
  • Yarn
Copy
npm install js-cookie
Copy
yarn add js-cookie

You can see the code for this hook below:

Copy
// utils/useUpdatePreviewRef.js
import { useEffect } from 'react'
import { useRouter } from 'next/router'
import Cookies from 'js-cookie'
import { repoName } from 'prismicConfiguration'

function getExitPreviewRoute(router) {
  const defaultPreviewExitUrl = '/api/exit-preview'
  const linkUrl = router.asPath ? `${defaultPreviewExitUrl}?currentUrl=${router.asPath}` : defaultPreviewExitUrl
  return linkUrl
}

function timeout(ms) {
  return new Promise(resolve => setTimeout(resolve, ms));
}

export default function useUpdatePreviewRef(previewRef, documentId) {
  const router = useRouter()
  const previewExitRoute = getExitPreviewRoute(router)
  useEffect(() => {
    const updatePreview = async () => {
      await timeout(1000)

      const rawPreviewCookie = Cookies.get('io.prismic.preview')
      const previewCookie = rawPreviewCookie ? JSON.parse(rawPreviewCookie) : null

      const previewCookieObject = previewCookie ? previewCookie[`${repoName}.prismic.io`] : null

      const previewCookieRef = previewCookieObject && previewCookieObject.preview
        ? previewCookieObject.preview
        : null

      if (router.isPreview) {
        if (rawPreviewCookie && previewCookieRef) {
          if (previewRef !== previewCookieRef) {
            return router.push(`/api/preview?token=${previewCookieRef}&documentId=${documentId}`)
          }
        } else {
          return router.push(previewExitRoute)
        }
      } else if (rawPreviewCookie && previewCookieRef) {
        return router.push(`/api/preview?token=${previewCookieRef}&documentId=${documentId}`)
      }
      return undefined
    }
    updatePreview()
  }, [])
}

3.2. Loader

The loader.js file is a component that is used as a buffer when the preview cookie is being loaded in the browser, it's essential for preview unpublished documents. It should live in the components folder.

Copy
// components/loader.js
import React from 'react'

/**
 * Site loader component
 */
 const Loader = () => (
  <div className="loader">
    <div className="ldsRipple"/>
    <style jsx>{`
      .loader {
        position: fixed;
        top: 50%;
        left: 50%;
        -moz-transform: translate(-50%, -50%);
        -webkit-transform: translate(-50%, -50%);
        -o-transform: translate(-50%, -50%);
        -ms-transform: translate(-50%, -50%);
        transform: translate(-50%, -50%);
      }
      .ldsRipple {
        width: 64px;
        height: 64px;
      }
      .ldsRipple:before, .ldsRipple:after {
        content: '';
        position: absolute;
        border: 4px solid #333;
        opacity: 1;
        border-radius: 50%;
        animation: lds-ripple 1s cubic-bezier(0, 0.2, 0.8, 1) infinite;
      }
      .ldsRipple:after {
        animation-delay: -0.5s;
      }
      @keyframes lds-ripple {
        0% {
          top: 28px;
          left: 28px;
          width: 0;
          height: 0;
          opacity: 1;
        }
        100% {
          top: -1px;
          left: -1px;
          width: 58px;
          height: 58px;
          opacity: 0;
        }
      }
      `}</style>
  </div>
)

export default Loader

3.3. Custom 404

The custom Next.js error file is necessary for previewing unpublished documents. It should live at pages/404.js and is needed to load the preview script, Link Resolver and hit the pages/api/preview.js route.

Copy
// pages/404.js
import Link from 'next/link'
import Head from "next/head"

export default function Custom404() {
  return <>
    <div>
      <Head>
          <title>404 - oops</title>
      </Head>
      <div className="fourohfour">
        <h1>404 - Page Not Found</h1>
        <Link href="/">
          <a>
            Go back home
          </a>
        </Link>
      </div>
      <style jsx>
        {`
          .fourohfour {
            margin: 0 auto;
            padding: 100px 0 100px 0;
            max-width: 700px;
          }
        `}
      </style>
    </div>
  </>
}

4. Set the preview prop and import helpers

After you hit the api/preview route which enables preview mode, you will need to pass the page function the preview data and use it inside the getStaticProps query for your page. For this, you will need to configure the previewRef prop which is empty when not in preview mode and which will contain this data once preview mode is triggered.

On the page, you will need to import the <Custom404 />, which is triggered when no page data is found and the <Loader /> component, which is loaded when there is no page with that UID found for that route.

You also import the useUpdatePreviewRef hook for your hot reloads and pass it the previewRef and post id. (post is the just a variable name for the returned document data.)

The example below is done on a dynamic page such as [uid].js.

Copy
// pages/[uid].js
import React from 'react'
import { useRouter } from 'next/router'

// Custom components
import Loader from '../components/Loader'
import Custom404 from './404'

// Project functions imported from wherever you declare them
import { Client } from '../prismicHelpers'
import { queryRepeatableDocuments } from '../utils/queries' //some helper queries for the getStaticPaths function
import useUpdatePreviewRef from '../utils/useUpdatePreviewRef'

/**
 * Post page component
 */
const Post = ({ post, previewRef }) => {
  const router = useRouter()
  if (router.isFallback) {
    return <Loader />
  }

  if (!post.id) {
    return <Custom404 />
  }

  useUpdatePreviewRef(previewRef, post.id)
  return <p>Your page content</p>
}

export async function getStaticProps({ params, previewData }) {
  const previewRef = previewData ? previewData.ref : null
  const refOption = previewRef ? { ref: previewRef } : null

  const post = (await Client().getByUID('post', params.uid, refOption)) || {}
  return {
    props: {
      previewRef,
      post,
    },
  }
}

export async function getStaticPaths() {
  const documents = await queryRepeatableDocuments((doc) => doc.type === 'post')
  return {
    paths: documents.map((doc) => `/${doc.uid}`),
    fallback: true,
  }
}

export default Post

Configuration issues?

If you need to see an example that uses these previews settings, check out our Next.js blog example.


5. Enable previews in your repository

In order to enable preview, you need to add the preview configuration inside your repository. Here are the following steps:

  1. Go to your repository, navigate to Settings > Previews and add the configuration for a preview.
  2. Enter the preview Site Name, Domain, and Link Resolver route. Add the /api/preview route for the Link Resolver field.
  3. You can add as many preview URL's as you need. This allows you to have a production server, staging server, development server, etc. Discover how to handle multiple environments in Prismic.

6. Trigger a preview in Prismic repository

To view a preview of your content changes, make a change, click 'Save' and click the 'eye' icon, this will either open the preview or ask you to pick one of your preview environments.

6.1. Share a preview link

When you click on the "Preview" button on your document page, a new tab will open a preview page. Once you're on a preview page, you can click on the bottom left button and copy the shareable link and share it with your team. Your team members don't need to have a Prismic account to see the previews.

Attention: Shareable links for unpublished documents does not currently work for Next.js


Related articles:


Was this article helpful?
Not really
Yes, Thanks

Can't find what you're looking for? Get in touch with us on our Community Forum.