Skip to main content

Setting Up Post and Page Previews

Faust.js has support for page and post previews. This allows you to view your WordPress content on the headless frontend as drafts or even before publishing.

Set Your Headless Secret#

Your headless secret is a value that is used to authenticate requests to WordPress. This enables you to view content that isn't yet published, on your Next.js frontend.

Copy your Headless Secret#

Your headless secret can be found in WP Admin -> Settings -> Headless. Copy this value.

The Headless WordPress admin interface with a red rectangle around the Secret Key field

Add your Headless Secret to your .env File#

Add the WP_HEADLESS_SECRET key to your .env file with the headless secret as the value. Your .env file should already have a value for either NEXT_PUBLIC_WORDPRESS_URL or WORDPRESS_URL. The file should look something like this:

.env
# Either WORDPRESS_URL or NEXT_PUBLIC_WORDPRESS_URL need to be populated. Not both!
#
# (Recommended) Setting WORDPRESS_URL instead of NEXT_PUBLIC_WORDPRESS_URL will limit requests to the WordPress backend
# to only come from the Node.js server.
#
# Setting NEXT_PUBLIC_WORDPRESS_URL instead of WORDPRESS_URL will allow requests to come from the client-side which may
# reduce site performance and put extra load on the WordPress backend.
NEXT_PUBLIC_WORDPRESS_URL=http://localhost:8080
# WORDPRESS_URL=http://localhost:8080
# Plugin secret found in WordPress Settings->Headless
WP_HEADLESS_SECRET=00000000-0000-0000-0000-000000000001

Ensure your Headless Secret is Passed to headlessConfig#

Typically, your Next.js app will have headlessConfig set in the _app.tsx file. The headless secret is passed via the apiClientSecret key. Make sure it is being set:

_app.tsx
import { headlessConfig } from '@faustjs/core';
import { HeadlessProvider } from '@faustjs/next';
import { client } from 'client';
import type { AppProps } from 'next/app';
headlessConfig({
wpUrl: process.env.WORDPRESS_URL || process.env.NEXT_PUBLIC_WORDPRESS_URL,
apiClientSecret: process.env.WP_HEADLESS_SECRET,
});
export default function MyApp({ Component, pageProps }: AppProps) {
return (
<>
<HeadlessProvider client={client} pageProps={pageProps}>
<Component {...pageProps} />
</HeadlessProvider>
</>
);
}

Create the Authorize Handler#

Next, you will need to create the authorizeHandler. This authenticates requests from WordPress. Create a page at /src/pages/api/auth/wpe-headless.ts with the following:

import { authorizeHandler } from '@faustjs/core';
export default authorizeHandler;

Create your Preview Page#

With your headless secret set and the authorizeHandler ready to handle requests, you can now create a Next.js page for previews. Create a file at /src/pages/preview.tsx with the following:

src/pages/preview.tsx
import { useRouter } from 'next/router';
import { PageComponent } from './[...pageUri]';
import type { Page, Post } from '@faustjs/core';
import { PostComponent } from './posts/[postSlug]';
import { client } from 'client';
export default function Preview() {
const { query } = useRouter();
const { usePreview } = client;
// Use the "p" and "page_id" queries to determine if the preview is a post or page
const { p, page_id } = query;
const isPage = !!page_id;
// Get the post or page from the usePreview hook.
const postOrPage: unknown = usePreview({
pageId: isPage ? (p as string) : undefined,
postId: !isPage ? (p as string) : undefined,
} as any);
if (postOrPage === null) {
return <>Not Found</>;
}
/**
* Below we return a <PageComponent /> or <PostComponent /> based
* on the preview type. We do this to maintain a consistent experience throughout
* previews and actual page/post renders.
*/
// If the preview is for a page, return a "page" component
if (isPage) {
return <PageComponent page={postOrPage as Page} />;
}
// Otherwise, return the "post" component
return <PostComponent post={postOrPage as Post} />;
}

Let's break down what is going on here:

First we are getting the p and page_id values from the Next.js Router query:

const { query } = useRouter();
const { p, page_id } = query;
const isPage = !!page_id;

We then use these values in our usePreview hook to fetch the correct post or page:

const postOrPage: unknown = usePreview({
pageId: isPage ? (p as string) : undefined,
postId: !isPage ? (p as string) : undefined,
} as any);

Finally, based on whether isPage is true, we will return a <PageComponent /> or <PostComponent /> respectively.

if (postOrPage === null) {
return <>Not Found</>;
}
if (isPage) {
return <PageComponent page={postOrPage as Page} />;
}
return <PostComponent post={postOrPage as Post} />;

We abstract the page and post layouts into components to be reusable so there is no divergence between preview pages/posts and actual pages/posts.

Navigating to Preview Pages#

Start by logging into your WordPress Admin. For this example, we'll create a new post.

So far, I've added a title and a simple line of text for the content. To view this post as a preview on your frontend, click the Preview link (1). From there, click, Preview in new tab (2):

WordPress post page using the Gutenberg editor with a red arrow to the preview and preview in new tab dropdowns

Notice the Publish button is also visible. This means the post has not been published yet and can not be viewed on the frontend without being authenticated.

Clicking on Preview in new tab should take you to your post preview page with the current preview content:

Post preview on the frontend in Next.js