The architecture

At the front of our blog is a modern web app built using Next.js. It’s a minimalistic framework for server-rendered React apps, made by the good folks at ZEIT.

The Next.js website pulls all content from a headless WordPress instance. The flexibility of WordPress allows it to be both monolithic and headless. In a headless architecture, WordPress uses the built-in REST API that any clients can use to fetch content from.

The entire setup is based on this starter kit for headless WordPress built by Postlight. It’s batteries-included, with themes, plugins, and even a fully-working Next.js frontend to kickstart your website. It also includes neat tools to automate most of our workflow and focus on our code.

Theme setup

“Now hold on,” you may ask, “If our WordPress is headless, then why would we need a theme?”

First of all, we will need to register some functions into our WordPress setup. One of which redirects every request to the REST API endpoint, since we’re not using WordPress to render our page.

And secondly, we need a custom REST API endpoint to fetch a post by its slug, since we’ll pass that from the frontend as an argument. WordPress has a special API to set up custom REST API routes in our theme. To learn more about these APIs, check out this guide.

Now try opening your WordPress site in the browser. You should be able to see that everything is now redirected to the REST API.

The plugins

WordPress has a next-generation editing experience called Gutenberg already in the pipeline. If you want to try it now, it’s available as an opt-in feature by installing a plugin. However, Gutenberg is still in its infancy. Some features that we need, like exposing blocks onto the API, are still a work-in-progress.

So for the purposes of our blog, I’m putting it aside. That doesn’t mean I haven’t been giving Gutenberg a try, though. I have forked Postlight’s starter and converted it to use Gutenberg.

The closest thing to Gutenberg that is readily available is Advanced Custom Fields. We use this to manage custom fields to add to our posts. For example, adding an extra field for post excerpts, as well as featured images in posts.

To be able to use ACF fields and custom menus in our frontend, we need to expose them in our REST API.

For ACF, we use the ACF to WP-API plugin. It requires no setup at all, and puts the custom fields in your post API response under the _acf key.

For our menus, wp-rest-api-v2-menus allows us to expose menus as WP-API endpoints. Thanks to this, we can make customisable site menus for our frontend. To access this menu from the REST API, we’ll have to register some nav menu locations. Put the following in your theme’s functions.php :

To access this menu, call the API generated by the plugin, adding the menu’s slug. For example: /wp-json/menus/v1/menus/header-menu .

The frontend

At Kata.ai, we are huge fans of React. In the JavaScript world, it already needs no further introduction. There are many reasons why it’s one of the most popular JavaScript frameworks in the world. Especially for single-page apps.

But content-heavy sites like blogs rely on SEO, so server-side rendering is still the way to go. Some popular solutions for server-side rendering include Razzle and Next.js, but Next.js stood out from the rest.

Next.js provides a clean, minimalistic API to get started quickly. By default, it provides the next command line, which runs a preconfigured web server. But you can also set up a custom server with your own request handlers and routing schemes if you want to. Next.js exposes server-side APIs that allows us to do that.

In this example, we use Express to do these things:

Create request handlers for some custom route paths

Create a fallback route to forward all requests outside of the defined route to Next.js

Start our server listener.

And that’s how you set up a custom Next.js server. Pretty cool, right? You can even swap Express withany other server library you want, like koa or hapi.

Fetching content + getInitialProps

In Next.js, each page has its own file inside the /pages directory. By default, it’ll be served relative to its path on the web server. For example, to create a page for posts we create post.jsx and page.jsx file inside the pages/ directory. When we start the Next.js server, we can access them from /post and /page , respectively.

By default Next.js uses query parameters to pass data to the page. But since we’ve set up a custom server handler for our Next.js app, we can override it to use route parameters.

WordPress has two built-in post types, post , and page . On our frontend server, we’ll add the routes for them just before our fallback handler. We create a route which accepts a slug parameter for both of them. This in return server-renders the page that you specified when you visit that route.

In this piece of code, the render() function takes your request and response object, along with the actual page relative to the pages/ directory, as well as some query parameters.

Then inside the component, we let getInitialProps() do all the hard work. You can use this to fetch anything you’d like from an API and prerender them in your page, it’s that simple.

Handling search

Implementing WordPress search on our Next.js frontend is straightforward. We can now use Next.js’ default routing scheme, which is based on query parameters.

For example, say we have a search bar component like this:

When the form is submitted, we can simply use next/router to redirect users to the search page and we pass the query to getInitialProps() .

We can give our brand-new search page a try! Type anything into your search box and submit it. If you get redirected to your search page, you’re good!

What about post previews?

One of the problems with headless WordPress is that by default, WordPress generates its own preview page. And we don’t want that.

Interestingly enough, we can create a custom “Preview” button link in our theme, using the preview_post_link filter. We need to set it up in our WordPress theme’s functions.php . This will change our preview URL to the one defined in this filter.

Here, change the link to a /_preview path which will collect the ID of the previewed post, as well as a wpnonce . This is required for authentication purposes. It’ll also set your preview domain based on whether you have WP_DEBUG enabled or not.

Then in our Next.js server we do the same as everything else, register the route and fetch the post in the page. But instead of using getInitialProps() , we use the standard componentDidMount() lifecycle. We need to use this because wpnonce authentication relies on browser cookies.

So when we click the “Preview” button on the WordPress editor, it now redirects to the Next.js app.