Post Previews in Headless WordPress with Nuxt 3

Francis Agulto Avatar

·

Post previews in WordPress allow users to view content exactly as it will appear on the live site before it is published. This feature is essential for authors, editors, and administrators to review and approve posts, ensuring that formatting, images, and layouts are correct and meet the intended design and content standards.

The default post preview functionality that WordPress core provides doesn’t work for headless WordPress setups. Luckily, the Faust.js plugin makes it possible to add support for post previews with headless sites.

This article will explain how I implemented post previews in a Nuxt 3 application using the Faust.js plugin.

Prerequisites

To benefit from this article, you should be familiar with the basics of working with the command line, headless WordPress development, Vue, and Nuxt.

Steps for setting up:

1. Set up a WordPress site and get it running.  Local by WP Engine is a good dev environment to use.

2. Head over to your WP Admin and install and activate the WPGraphQL and Faust.js WordPress plugins.

3. Clone down the Nuxt repo for this project by copying and pasting this command in your terminal:

`npx degit Fran-A-Dev/nuxt3-headlesswp-post-previews#main my-project`
Code language: JavaScript (javascript)


4.  CD into your Nuxt project and open it up. Create a .env file in the root of the Nuxt project. Open that file in a text editor and paste in the following:

FAUST_SECRET_KEY=your-secret-key
NUXT_PUBLIC_WORDPRESS_URL=https://your-wordpress-url
NUXT_PUBLIC_FRONTEND_SITE_URL=http://localhost:3000
Code language: JavaScript (javascript)

5. Go back to your WP Admin. Navigate to Settings > Faust and grab the secret key from the field it autopopulates in, as shown here. Copy that key:

While you are in the WP Admin, copy your WordPress URL as well. Then head back over to your .env file in your Nuxt project and add your secret key and URL appropriately.

6. Run npm install to install dependencies.

7. Run npm run dev to start the dev server locally.

You should now be able to navigate to your WP Admin, add or edit a post, click the preview link, and see the post preview work in your Nuxt frontend before clicking the publish button:


Stoked!!!! The headless post previews flow works in Nuxt!

The Faust.js Plugin

Faust.js is a plugin that was created to accompany the Faust.js toolkit, a suite of tools for adding common features such as authentication, post previews, and more to headless WordPress sites.

The Faust toolkit handles everything from fetching posts and pages to managing previews and authenticated endpoints. While the official implementation is built around React and Next.js, the underlying ideas— data fetching, post previews, and token-based authentication—are framework-agnostic.   This is what we will take advantage of in this article! Stoked!!!

Translating the Faust.js Auth Flow to Nuxt.js

Previewing unpublished or draft content requires WordPress to verify that the requester is an authenticated user with proper permissions. Faust.js achieves this via an OAuth2-style sequence of short-lived codes and tokens. Here’s the typical flow—and how each step maps to our Nuxt implementation:

  1. User clicks “Preview in new tab” in WordPress
  1. WordPress sends the user to Nuxt with preview=true and p=[post_id] query parameters
  1. Middleware intercepts and redirects to WordPress’s /generate endpoint
  1. WordPress authenticates the user and redirects back to the Nuxt app with a one-time OAuth authorization code in the URL query parameters from Faust and WordPress
  1. Preview page loads and calls our API endpoint with the code
  1. API endpoint exchanges code for tokens using faust/index.js
  1. API uses the access token to fetch preview data from WordPress GraphQL
  1. API sets refresh token in HTTP-only cookie and returns post data
  1. Preview page renders the content

In subsequent preview requests, if the cookie exists, the API will try to use the refresh token first before falling back to the authorization code.

In the next sections, let’s explain each of the files that make this work. If you want to dive into each line of code in the files, I will link to them in the sections.

The Nuxt Config

The first file we will take a look at is the nuxt.config.js file at the root of the project.

The Nuxt configuration maps environment variables to the application’s runtime config, separating sensitive server-side values (FAUST_SECRET_KEY) from public client-accessible URLs.

This makes WordPress connection details available throughout the app via the useRuntimeConfig() composable.

Middleware

The second file that we will go over is the preview.global.js file in the middleware folder.

This middleware file serves as the traffic controller for WordPress post previews in our project, intercepting and routing preview requests.

It handles two critical scenarios: first, when an editor clicks “Preview” in WordPress, it redirects them to WordPress’s authentication endpoint with a properly configured redirect URI; and second, it allows authenticated preview requests with a valid authorization code to access the preview page directly.

The most essential code is the redirect itself:

return navigateTo(
      `${config.public.wordpressUrl}/generate?redirect_uri=${config.public.frontendSiteUrl}/preview?preview_id=${previewId}`,
      { external: true }
    );
Code language: JavaScript (javascript)

This builds the secure authentication pathway between WordPress and our Nuxt application.

Server API Route

Next, let’s go over the preview.get.js file in the server/api folder.

This server route functions as the authentication and data retrieval engine for WordPress post previews. It handles the secure exchange of authorization codes for access tokens and then uses those tokens to fetch unpublished content.

It implements a multi-tier authentication approach, first trying to use a refresh token from a cookie, falling back to the one-time authorization code if needed, and then making an authenticated GraphQL request to WordPress with the crucial parameter asPreview: true to access draft content.

The most important lines are the authentication header Authorization: \Bearer ${tokens.accessToken}\ which allows secure access to unpublished content.

The cookie handling setCookie(event, "faust_preview_rt", tokens.refreshToken, {...}) enables a seamless preview experience for subsequent requests.

The faust/index.js

The next file up is the index.js file in the faust folder.

This Faust utility file serves as the authentication bridge between our Nuxt application and WordPress, providing a secure mechanism for exchanging authorization codes and refresh tokens for valid access tokens. It implements a central approach with the token exchange logic in a single private function while exposing two specific public methods for different authentication scenarios.

The most essential parts of this implementation are the WordPress REST API endpoint construction:

const authorizeUrl = `${config.public.wordpressUrl}/?rest_route=/faustwp/v1/authorize`;

  const response = await fetch(authorizeUrl, {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "x-faustwp-secret": apiClientSecret,
    },
    body: JSON.stringify(payload),
  });
Code language: JavaScript (javascript)

This targets the FaustWP plugin’s authentication endpoint and the security header, which authenticates our app to WordPress using the secret key.

Rendering the preview page

The final step before we see our preview data on the browser is to render it. We do this in the preview.vue file that lives in the pages folder.

This preview page component serves as the frontend interface for displaying WordPress draft posts, orchestrating the authentication flow, and rendering unpublished content.

It manages the preview session by first checking for an existing refresh token cookie, falling back to the URL’s authorization code when needed, and then making an API request to fetch the protected content with the appropriate credentials.

The authentication logic in the file is as follows: 

if (cookie.value) {
  queryParams = { refreshToken: cookie.value, previewId };
} else if (code) {
  queryParams = { code, previewId };
Code language: JavaScript (javascript)

This implements a seamless authentication flow.

Then, the server makes a request to retrieve the draft content while maintaining proper separation between the presentation layer and the authentication logic:

const {
  data: fetchData,
  pending,
  error: fetchError,
} = await useFetch("/api/preview", {
  method: "GET",
  query: queryParams,
});
Code language: JavaScript (javascript)

Conclusion

We hope this article helped you understand how post previews in Nuxt 3 work in my example project with the Faust plugin!

As always, we’re super stoked to hear your feedback and learn about the headless projects you’re working on, so hit us up in the Headless WordPress Discord!