Previews In Headless WordPress With NextJS

Will Johnston Avatar

·

One of the first challenges you’ll face when you try out Headless WordPress is the lack of preview support. You cannot retrieve unpublished content through an API without the user authenticating first. On a traditional WordPress site, this isn’t an issue because the frontend is on the same domain as the backend, so you can use a cookie for authorization. When you use a frontend hosted on a separate domain from WordPress, your WordPress cookies will not work. To see unpublished content, you will need to set up a way to make authenticated requests to the WordPress API. This can be tricky because you do not want to allow unauthenticated users to see your unpublished content, so you need to avoid hard coding authentication secrets into your frontend code. This post will help you show you a few tools you can use to get previews working in NextJS.

Setting Up WordPress For Headless

If you haven’t already, check out our post about setting up WordPress for headless. For this post you will need to install and configure the following plugins (outlined in the post linked above):

  1. WPGraphQL
  2. Faust.js

Make sure you follow the instructions in the post for configuring Faust.js. In the Faust.js settings (found in Settings > Faust), set Front-end site URL to http://localhost:3000/. This will eventually be the URL where our frontend site is served.

The next thing you will want to grab and store from the WPEngine Headless settings is the Secret Key. Below is an example screenshot that shows the Front-end site URL and Secret Key:

Your Faust.js settings will look something like this. Make sure to note the Secret Key as you will need it later.

Building The Frontend Site With NextJS

Before building out your frontend site, you will need to make sure you have Node.js and NPM installed on your computer. If you are using a Mac, it is recommended that you install NVM, which will help you manage your Node versions.

Once you have Node and NPM installed on your machine, run the following commands:

npx create-next-app previews
cd previews
rm -r ./pages/*
npm i @wpengine/headless @apollo/client graphql
npm i typescript @types/react @types/react-dom @types/node -D
Code language: PHP (php)

Now your site is set up, but you still need to enable authentication with WordPress and build out a home page.

Authentication

To view previews in WordPress, you need to have higher access to your WordPress site. WordPress won’t show previews to you unless you are an authenticated user. You don’t notice this with a traditional WordPress site because the URL you preview posts typically shares the same domain and cookies as your WordPress admin panel. For headless WordPress, you have to find another way of authenticating and making requests for previews. This is where the WPEngine Headless plugin comes in handy.

The WPEngine Headless plugin exposes routes that allow for an OAuth flow to obtain an access token used for authenticated requests such as previews. The flow looks as follows:

  • User requests a secure route (i.e., draft post).
  • User redirects to WordPress to login.
  • WordPress redirects back to the frontend with a temporary code.
  • The frontend server exchanges the code for an access token.
  • The access token stores in a cookie.
  • The user finally redirects back to the original URL and uses the access token in the cookie to make the authenticated request.

Lucky for us, the @wpengine/headless plugin provides a Node authorization handler to facilitate exchanging the authorization code for an access token, storing the token in a cookie, and redirecting back to the appropriate frontend route.

Authorization Handler

To take advantage of the authorization handler in your NextJS app with @wpengine/headless you can create a new NextJS API route in /pages/api/auth/wpe-headless.ts with the following code:

import { authorizeHandler } from '@wpengine/headless';

export default authorizeHandler;
Code language: JavaScript (javascript)

authorizeHandler accepts a Node request (i.e. IncomingMessage) and response (i.e., ServerResponse) and will work with any Node-based server library. This is all you need to get authentication working in your NextJS app. Now let’s get started building out the application.

Building Your Frontend NextJS Page For Previews

NextJS is a React-based framework, and @wpengine/headless is written to work alongside NextJS and React.

HeadlessProvider

The first thing you need to do in order to integrate with @wpengine/headless is invoke the HeadlessProvider in your /pages/_app.tsx. Use the following code:

import React from 'react';
import { AppContext, AppInitialProps } from 'next/app';
import { HeadlessProvider } from '@wpengine/headless/react';

export default function App({
  Component,
  pageProps,
}: AppContext & AppInitialProps) {
  return (
    <HeadlessProvider pageProps={pageProps}>
      <Component {...pageProps} />
    </HeadlessProvider>
  );
}
Code language: JavaScript (javascript)

The HeadlessProvider is the glue that allows the framework to communicate with WordPress. It needs to exist at the top level of your application wherever you want to hook into WordPress and take advantage of the GraphQL API.

Routes and Hooks

To support previews, you need two pages. One page handles all public routes (/pages/[[…page]].tsx) and another will handle private preview routes (/pages/preview/[[…page]].tsx). [[…page]].tsx is how you describe a catch-all route in Next. Put the following code in /pages/[[…page]].tsx:

import React from 'react';
import {
  useUriInfo,
  getNextStaticPaths,
  getNextStaticProps,
} from '@wpengine/headless/next';
import { GetStaticPropsContext } from 'next';
import Posts from '../lib/components/Posts';
import Post from '../lib/components/Post';

export default function Page() {
  const pageInfo = useUriInfo();

  if (!pageInfo) {
    return <></>;
  }

  if (pageInfo.isPostsPage) {
    return <Posts />;
  }

  return <Post />;
}

export function getStaticPaths() {
  return getNextStaticPaths();
}

export function getStaticProps(context: GetStaticPropsContext) {
  return getNextStaticProps(context);
}
Code language: JavaScript (javascript)

Ignore the errors you get about <Posts /> and <Post /> for now, we will create those components soon. A couple interesting things to note with this component:

  1. getNextStaticProps is used to enable Static Site Generation (SSG) with Next. It knows how to get the URL information on the server-side and make the necessary query for a list or single post. It will then cache the queries so the queries can be used client-side without making network requests. This is crucial for site performance and SEO.
  2. useUriInfo is a hook provided by @wpengine/headless. It will get the current URL and query the WordPress GraphQL API to get information about the URL. If the route has a list of posts (i.e. pageInfo.isPostsPage we’ll show the Posts component, otherwise we show the Post component.

Let’s create the Post component. In /lib/components/Post.tsx place the following code:

import React from 'react';
import { usePost } from '@wpengine/headless/next';

export default function Post() {
  const post = usePost();

  return (
    <div>
      {post && (
        <div>
          <div>
            <h5>{post.title}</h5>
            <div
              dangerouslySetInnerHTML={{
                __html: post.content ?? '',
              }}
            />
          </div>
        </div>
      )}
    </div>
  );
}
Code language: JavaScript (javascript)

The component above is used to render an individual post. It takes advantage of the usePost hook provided by @wpengine/headless.

Next, let’s create the Posts component. In /lib/components/Posts.tsx put the following code:

import React from 'react';
import Link from 'next/link';
import { usePosts } from '@wpengine/headless/react';

export default function Posts() {
  const posts = usePosts();

  return (
    <div>
      {posts &&
        posts.nodes.map((post) => (
          <div key={post.id} id={`post-${post.id}`}>
            <div>
              <Link href={post.uri}>
                <h5>
                  <a href={post.uri}>{post.title}</a>
                </h5>
              </Link>
              <div
                dangerouslySetInnerHTML={{
                  __html: post.excerpt ?? '',
                }}
              />
            </div>
          </div>
        ))}
    </div>
  );
}
Code language: JavaScript (javascript)

The component above is used to render a list of posts. It uses the usePosts hook provided by @wpengine/headless to get the appropriate list of posts.

Running The Application

Now you should have a public page to display both a list of posts. Before you can run it, you will need to configure a .env.local file with the appropriate environment variables needed by @wpengine/headless. Create a file in the root of your project called .env.local and configure it as follows:

# Base URL for WordPress
NEXT_PUBLIC_WORDPRESS_URL=http://yourwpsite.com

# Plugin secret found in WordPress Settings->Headless
WP_HEADLESS_SECRET=YOUR_PLUGIN_SECRET
Code language: PHP (php)

You will need to set the NEXT_PUBLIC_WORDPRESS_URL to the root URL of your WordPress site. If you remember earlier in this post, you were asked to save your Secret Key from your WordPress headless settings, that should go in your .env.local as your WP_HEADLESS_SECRET value.

NOTE: It is important to only put NEXT_PUBLIC_ in front of environment variables if you intend for them to be available to the client. You do not want WP_HEADLESS_SECRET visible to the client, so it does not need the prefix.

Now that you have your .env.local configured, run your site with the following command:

npm run dev

A Node server will start on port 3000 by default: http://localhost:3000.

Adding A Page For Previews

After verifying that your site works and you can see a list of posts and individual posts, the last step is to set up a page for previews. Create a file called /pages/preview/[[…page]].tsx and put the following code in it:

import React from 'react';
import Post from '../lib/components/Post';

export default function Page() {
  return <Post />;
}
Code language: JavaScript (javascript)

The code for previews is a little bit cleaner than the public page, and for good reason. You only need to preview individual posts, so there is no need to do anything else. Also, note that the preview page does not use getStaticProps or getStaticPaths. This is intentional, as we only need to get previews client-side.

Now that you have this component in place, you can run your site and then test if previews work by going to your list of posts in wp-admin and clicking to preview a draft post (you might have to create a draft post if you don’t have one already).

That’s all there is to it, you have successfully set up a headless WordPress site with a NextJS frontend, and you can preview unpublished posts!