HeadstartWP is a comprehensive Next.js solution for headless WordPress sites. Learn how to set up, configure & solve some common issues.
TL;DR;
Pressed for time but still want the lowdown on HeadstartWP? Here’s a quick overview of what you need to know to get started with the best headless WordPress framework:
- Headless solution that pairs WordPress, React.js & Next.js
- It’s completely free & open source
- Includes multisite & draft preview support
- Built-in support for translation & native support for the Polylang plugin
- Route mapping utilizing Next.js’ catch-all routes feature
- All the Next.js rendering options (SSG, ISR & SSR)
- Huge performance advantages
- A Content Block API that allows easy rendering of Gutenberg blocks
Getting Setup Quick
npx create-next-app --use-npm -e https://github.com/10up/headstartwp/tree/trunk/projects/wp-nextjs
Table of Contents
- It’s here! HeadstartWP
- The Guiding Philosophy
- Quick Start with HeadstartWP
- HeadstartWP Fresh Install
- Step 1: Install the Headstart WordPress Plugin
- Step 2: Setting Up Your Next.js Project
- Step 3: Creating Your headless.config.js
- Step 4: Creating and Configuring Your .env File
- Step 5: Setting Up Your next.config.js File
- Step 6: Building Your _app.js File
- Step 7: Creating Your Preview Endpoint
- Step 8: Creating Your First Route
- Step 9: SSR/SSG: The Final Piece
- Troubleshooting
- Frequently Asked Questions
It’s here! HeadstartWP

Hey there, WordPress gurus! Are you ready for something exciting? Because I’ve been bouncing off the walls waiting to introduce you to our supercharged solution for WordPress sites – HeadstartWP!
Brought to you by the wizards at 10up — I’m proud to call myself a team member, HeadstartWP is a game-changing headless solution that pairs the world’s most popular CMS, WordPress, with the unstoppable force of Next.js, the most widely used React.js framework. And get this – it’s free and open source!
Packed with Power
What makes HeadstartWP so unique? Let me spill the beans. The headless team poured years of experience, expertise, and lessons learned from supporting countless headless sites into creating HeadstartWP, providing you with a toolbox full of goodies that makes decoupled site building a breeze. It’s like they’ve given you a magic wand that effortlessly conjures up multisites, handles translations with ease, gives you a sneak peek at your draft content before publishing, and so much more.
When it comes to headless WordPress implementation, consider HeadstartWP your essential Swiss Army knife. Starting off, it’s impressively lightweight yet robust, providing exactly what you need without any unnecessary fluff. Moving forward, it shines in its performance optimization, seamlessly supporting all the Next.js rendering patterns that you’ve grown to love, thereby significantly boosting your PageSpeed scores. Finally, let’s not overlook its innate multilingual capabilities, effortlessly opening up your website to a truly global audience. In short, with HeadstartWP, you’ve got the complete package.
Completely Infrastructure Agnostic
Here’s the real kicker about HeadstartWP – it’s completely infrastructure agnostic. That’s right! With this level of flexibility, you’re free to deploy your site wherever you see fit. Perhaps you’re more comfortable with a managed platform such as WordPress VIP or WP Engine? Or maybe you prefer to utilize your own AWS or Azure stack? Heck, you can even opt for a blend of both. With HeadstartWP, you’re firmly in the driver’s seat, empowered to navigate the route that aligns best with your needs.
Already used and loved by some of the world’s most well-known and high-traffic brands, HeadstartWP is constantly evolving with every iteration, making it one of the best solutions for headless WordPress implementation. And trust me, this is just the beginning. So, buckle up, and let’s embark on this headless WordPress journey with HeadstartWP together!
Behind the Code: The Guiding Philosophy of HeadstartWP
Ready to delve further into what makes HeadstartWP tick? This framework isn’t a haphazard creation that popped up out of the blue. In fact, the formation and evolution of HeadstartWP has been driven by a key set of principles. So, let’s pause for a moment to appreciate these guiding lights that are integral to the uniqueness of HeadstartWP.
Using a Proven Foundation
First off, 10up doesn’t believe in reinventing the wheel. Why should they, when there’s a rock-solid foundation already available? That’s right, folks, I’m talking about Next.js. It’s the top-dog, full-stack React framework that has won the hearts of developers all over the world. It was chosen to build HeadstartWP on this solid ground, allowing focus on what 10up does best: solving the challenges of creating headless WordPress sites.
HeadstartWP Reduces Complexity
The main goal of HeadstartWP was to reduce the complexity of building headless sites. 10up wanted to make creating headless WordPress sites as easy as pie, similar to creating traditional WordPress sites. The aim was to save developers the headache of having to figure out how to wire up the Next.js application with WordPress, and instead, let them focus on the important aspects of the site.
The Sky’s the Limit
But 10up’s not just about making things easier. They’re also about pushing boundaries and exploring new possibilities. It’s all about sparking creativity and letting engineers discover new ways of building and scaling sites. With HeadstartWP, you can do everything from shipping component libraries for brand consistency and code reuse, to building complex app-like experiences. You can leverage the power of serverless technology or even host completely at the Edge. The world is your oyster when you go headless with HeadstartWP.
Low Cost
What’s more, 10up has designed HeadstartWP to be a thin layer that interacts with WordPress, built on top of a solid foundation. This means maintenance costs are kept low, as the most complex parts are handled by Next.js, which is maintained by the innovative folks at Vercel, with a little help from their friends at Google.
HeadstartWP Keeps it Simple
Finally, 10up is all about keeping things simple. Currently, HeadstartWP doesn’t work with WPGraphQL. While GraphQL is great for the right projects, it doesn’t always add value for most headless sites and can introduce additional complexity and engineering time. That said, 10up might add support for GraphQL in future releases, but for now, they’re keeping our stack straightforward and effective.
Quick Start with HeadstartWP
Feeling eager to jumpstart your journey with HeadstartWP? Well, you’re in for a treat because setting up HeadstartWP is a breeze! Just a few steps and you’ll be all set.
Before we dive into it, let’s talk about system requirements. You’ll need:
- Node.js 16 or later
- NPM version 7 or above
- WordPress 5.9 or higher (earlier versions might work, but haven’t been tested)
Here’s how you get started:
First, if you’re new to Next.js, take a look at the Next.js docs. It will give you a solid understanding of the foundation HeadstartWP is built on.
After that, the quickest way to get rolling with HeadstartWP is by using create-next-app
with the official starter project. Here’s the command you need:
npx create-next-app --use-npm -e https://github.com/10up/headstartwp/tree/trunk/projects/wp-nextjs
Once the project is set up, run npm run dev
in your terminal, and voila! You can then open your newly created HeadstartWP site at http://localhost:3000 in your browser.
Environment Variables
A quick note on environment variables: the starter project will point to js1.10up.com
by default. If you need to point to a different location, change the NEXT_PUBLIC_HEADLESS_WP_URL
variable, or create a .env.local
file to override the default environment variables. And if you’re developing locally using HTTPS with WordPress and don’t have valid certificates, make sure to add NODE_TLS_REJECT_UNAUTHORIZED=0
to avoid any hiccups.
And just like that, you’re on your way to creating amazing headless WordPress sites with HeadstartWP!
HeadstartWP Fresh Install
We’re going to set up HeadstartWP in a spanking new Next.js project. It’s like moving into a clean, unfurnished apartment and making it your own with your unique style! The end goal? Having a project that’s primed and ready for you to create your magic.
Alright, let’s move on to step one – setting up your Next.js project! This is all about getting your Next.js project off the ground.
Install the Headstart WordPress Plugin
First things first, we’re going to install a super handy plugin that’ll make our headless WordPress site work seamlessly with our new setup. Let’s dive right in!
Installing via Composer (Recommended)
The best way to install this plugin is using Composer. It’s a tool for dependency management in PHP, and it makes installing plugins a breeze. Here’s how you do it:
composer require headstartwp/headstartwp
To set up the path for installing this as a WordPress Plugin, you’ll need to add the following to your composer.json
file:
{
"name": "your-vendor-name/your-project-name",
"require": {
"composer/installers": "^1.0",
"headstartwp/headstartwp": "^1.0.0",
},
"extra": {
"installer-paths": {
"plugins/{$name}/": [
"type:wordpress-plugin"
]
}
},
"config": {
"allow-plugins": {
"composer/installers": true
}
}
}
Manual Install
If you’re not a fan of Composer, no worries. You can also install the plugin manually. All you need to do is download the plugin’s zip file, move it to wp-content/plugins
in your WordPress directory, and then activate the plugin from your WordPress admin dashboard.
Configuring the WordPress Headless Plugin
After you’ve installed and activated the plugin, it’s time to configure it.
First, you need to tell the plugin where your front-end site is located. To do this, go to your WordPress admin dashboard and click on Settings -> General
. Scroll down until you find the Headless Frontend URL field. Enter the URL for your headless site in this field.

And there you have it! Your HeadstartWP plugin is now set up and ready to rock ‘n roll.
Setting Up Your Next.js Project
First things first, you gotta fire up your terminal and type in this command:
npx create-next-app@latest --use-npm
Boom! Just like that, you’ve got the latest version of create-next-app
installed in your project using npm. It’s like the starter dough for your Next.js project.
But wait, we’re not done yet! To make this project really come alive, we need to install a couple more packages. Think of these as the secret ingredients to your recipe. Here’s the command you need:
npm install --save @headstartwp/core @headstartwp/next
With this command, you’re adding the @headstartwp/core and @headstartwp/next packages to your project. These packages are the backbone of your HeadstartWP framework, bringing in all the goodness that it has to offer.
Creating Your headless.config.js
Now that we’ve got our Next.js project and essential packages all setup, it’s time to dive into the next part of our journey. Let’s create a new file at the root of our project called headless.config.js
.
Think of headless.config.js
as the instruction manual that guides our HeadstartWP framework. It tells the framework how to behave and interact with our Next.js project.
Pop open your code editor, and let’s get to work on this new file. Here’s what you’ll want to type into headless.config.js
:
/**
* Headless Config
*
* @type {import('@headstartwp/core').HeadlessConfig}
*/
module.exports = {
sourceUrl: process.env.NEXT_PUBLIC_HEADLESS_WP_URL,
useWordPressPlugin: true,
};
As you can see, this file is exporting a configuration object. The sourceUrl
property is where you’ll want to put the URL of your WordPress site. You can put this URL in an environment variable called NEXT_PUBLIC_HEADLESS_WP_URL
.
The useWordPressPlugin
property is set to true, which means we’re telling our project to use the WordPress plugin. This plugin is a powerful tool that lets you use WordPress as a headless CMS with your Next.js project.
Creating & Configuring Your .env File
Alright, you’ve been doing awesome so far! Now let’s move on to the next stage in our step one: dealing with environment variables.
You’re probably like, “Wait, what are environment variables?” Well, they’re kind of like the secret codes that your app needs to work properly. They hold crucial data like API keys, database passwords, and in our case, the URL of your WordPress site.
Let’s start by creating a .env
file (or you can use a .env.local
file if you prefer) in the root of your project. Here’s what you should put in it:
NEXT_PUBLIC_HEADLESS_WP_URL=https://my-wordpress.test
This is the URL of your WordPress site. You can replace https://my-wordpress.test with your actual site’s URL.
By the way, you can name this environment variable whatever you like! Just remember to update the sourceUrl
property in your headless.config.js
file to match the name of your new environment variable.
One more thing! If you’re developing on your local machine and your WordPress site uses HTTPS but doesn’t have a valid certificate, you might run into some issues. But don’t worry, I’ve got a quick fix for you! Just add this line to your .env
file:
NODE_TLS_REJECT_UNAUTHORIZED=0
This tells Node.js to ignore the self-signed certificate error and lets you carry on with your work uninterrupted.
Setting Up Your next.config.js File
Awesome job on setting up those environment variables! Now, let’s dive into the next part of step one: creating your next.config.js
file.
Think of next.config.js
as the brains behind your Next.js project. It’s where you specify your custom configuration settings, which helps tailor your project to your specific needs.
Grab your code editor, because we’re about to create a new file. Here’s what you should type into your next.config.js
file:
const { withHeadlessConfig } = require('@headstartwp/next/config');
const headlessConfig = require('./headless.config');
/**
* Update whatever you need within the nextConfig object.
*
* @type {import('next').NextConfig}
*/
const nextConfig = {};
module.exports = withHeadlessConfig(nextConfig, headlessConfig);
First, we’re bringing in the withHeadlessConfig
function from the @headstartwp/next/config
package. We’re also importing our headless.config.js
file that we created earlier.
Then, we’re creating an object called nextConfig
. You can put any custom configurations you need for your Next.js project in this object.
Finally, we’re exporting the result of calling the withHeadlessConfig
function with nextConfig
and headlessConfig
as arguments. This merges your custom Next.js configuration with the HeadstartWP configuration.
Building Your HeadstartWP _app.js File
Now that we’ve got our environment variables and configuration files all set, it’s time to create a custom _app.js
file. This file is like the master switch that controls all the pages in your Next.js project. We’re going to wrap our entire application with the HeadlessApp
component from the @headstartwp/next
package.
Let’s get into the nitty-gritty of creating this file. Here’s what you’ll want to type into your src/pages/_app.js
file:
import { HeadlessApp } from '@headstartwp/next';
import Link from 'next/link';
import Router from 'next/router';
import '../styles.css';
const MyApp = ({ Component, pageProps }) => {
// only HeadlessApp needs fallback and themeJson, so we remove them from the props we pass down to the pages
// eslint-disable-next-line react/prop-types, no-unused-vars
const { fallback = {}, themeJson = {}, ...props } = pageProps;
return (
<HeadlessApp
pageProps={pageProps}
settings={{
// instruct the framework to use Next.js link component or your own version
linkComponent: Link,
}}
>
<Component {...props} />
</HeadlessApp>
);
};
export default MyApp;
In this file, we’re importing the HeadlessApp
component, the Link
component from next/link
, and the Router
from next/router
. We’re also importing our main CSS file.
Then we’re defining a function called MyApp
that takes an object as a parameter. This object includes Component
and pageProps
. Inside this function, we’re removing fallback
and themeJson
from pageProps
and passing the rest of the props down to the pages.
Finally, we’re returning the HeadlessApp
component, which wraps around the Component
that represents the current page. We’re passing pageProps
and a settings
object to HeadlessApp
. In settings
, we’re setting linkComponent
to Link
, which tells the framework to use the Next.js link component.
Creating Your Preview Endpoint
You’re doing fantastic! Now let’s step into the next part of our setup journey: setting up the preview endpoint.
In simple terms, a preview endpoint allows you to take a sneak peek at your content before it goes live. The HeadstartWP WordPress plugin expects this preview endpoint to be at /api/preview
.
Let’s get to work and enable support for previews! We’re going to create a new file at src/pages/api/preview.js
. Here’s what you should type into this file:
import { previewHandler } from '@headstartwp/next';
/**
* The Preview endpoint just needs to proxy the default preview handler
*
* @param {*} req Next.js request object
* @param {*} res Next.js response object
*
* @returns
*/
export default async function handler(req, res) {
return previewHandler(req, res);
}
First, we’re importing the previewHandler
function from the @headstartwp/next
package.
Then, we’re defining an asynchronous function called handler
that takes two parameters: req
(the Next.js request object) and res
(the Next.js response object). This function simply calls the previewHandler
function with req
and res
as arguments.
Creating Your Revalidate Endpoint
Rocking on! Now, let’s tackle the next part of our setup process: setting up the revalidate endpoint. This one’s pretty cool because it supports Incremental Static Regeneration (ISR) revalidation, which is triggered by WordPress. ISR allows you to update static pages after they’ve been generated, which is super handy for keeping your content fresh!
But before we can use this nifty feature, you need to make sure you’ve got the WordPress plugin enabled and the ISR option activated in your WordPress settings.

Once that’s done, it’s time to create a new file at src/pages/api/revalidate.js
. Here’s what you should type into this file:
import { revalidateHandler } from '@headstartwp/next';
/**
* The revalidate endpoint just needs to proxy the default revalidate handler
*
* @param {*} req Next.js request object
* @param {*} res Next.js response object
*
* @returns
*/
export default async function handler(req, res) {
return revalidateHandler(req, res);
}
First, we’re importing the revalidateHandler
function from the @headstartwp/next
package.
Next, we’re defining an asynchronous function called handler
that takes two parameters: req
(the Next.js request object) and res
(the Next.js response object). This function simply calls the revalidateHandler
function with req
and res
as arguments.
Creating Your HeadstartWP First Route
Well, look at you, almost at the finish line! The next step is creating your first route. This is where the magic happens – we’re going to create a catch-all route called pages/[...path].js
. This route will be responsible for rendering individual posts and pages. Pretty neat, right?
By setting up a [...path].js
route, the HeadstartWP framework will automatically detect and extract URL parameters from the path argument. It’s like having your own personal detective, searching for clues in your URL paths!
Here’s the code you’ll need to create this route:
import {
usePost,
fetchHookData,
addHookData,
handleError,
usePosts
} from '@headstartwp/next';
import { BlocksRenderer } from '@headstartwp/core/react';
const params = { postType: ['post', 'page' ] };
const SinglePostsPage = () => {
const { loading, error, data } = usePost(params);
if (loading) {
return 'Loading...';
}
if (error) {
return 'error...';
}
return (
<div>
<h1>{data.post.title.rendered}</h1>
<BlocksRenderer html={data.post.content.rendered} />
</div>
);
};
export default SinglePostsPage;
Once you’ve got that set up, you can visit any single post or page (like http://localhost:3000/hello-world) and voila! You’ll see both the title and the content of that post or page.
And guess what? Date URLs will work too! So you could visit a URL like http://localhost:3000/2022/10/2/hello-world and it’ll work like a charm.
And there you go! You’ve created your first route and your Next.js project with HeadstartWP is looking pretty awesome. You’ve got this!
SSR/SSG: The Final Piece of HeadstartWP
Alrighty, we’re almost there! The last bit of setup we need to do is adding support for Server-Side Rendering (SSR) and Static Site Generation (SSG). Remember that single post route we created earlier? Well, it’s fetching data on the client side right now, which is alright but not ideal. We want our data to be fetched on the server side, and that’s where SSR/SSG come into play.
What we need to do is add the following code to our pages/[...path].js
file:
// or export async function getServerSideProps(context)
export async function getStaticProps(context) {
try {
// make sure to pass the same params to fetchHookData and usePost
const usePostData = await fetchHookData(usePost.fetcher(), context, { params });
return addHookData([usePostData], {});
} catch (e) {
return handleError(e, context);
}
}
Here’s what’s happening in this code:
- We’re exporting an asynchronous function named
getStaticProps
. It’s one of Next.js’s data fetching methods that lets us fetch data at build time. - Inside the function, we’re trying to fetch hook data using the
fetchHookData
function andusePost.fetcher()
as parameters. We’re also passing in the current context and the parameters we defined earlier. - We then return the fetched data using
addHookData
. - If there are any errors during this process, we handle them using the
handleError
function.
Once you’ve added this to your file, go ahead and refresh your page. Bam! Your data is now being fetched on the server side. You’ve successfully set up your HeadstartWP project. Give yourself a pat on the back – you did great!
Troubleshooting
I encountered an error message Error: The Edge Function "src/middleware" is referencing unsupported modules...
during deployment. How can I resolve this issue?

This error is typically caused by a compatibility problem with the Next.js version you are using, particularly version 13.4.4. To fix it, you should consider upgrading to the latest available version, which is 13.4.7 as of the time of writing this.
Error: The Edge Function "src/middleware" is referencing unsupported modules:
- index.js: ./resvg.wasm?module, ./yoga.wasm?module
NOW_SANDBOX_WORKER_EDGE_FUNCTION_UNSUPPORTED_MODULES: The Edge Function "src/middleware" is referencing unsupported modules: - index.js: ./resvg.wasm?module, ./yoga.wasm?module
$ npm i next@latest
Why am I getting Unsupported engine
with running npm install
?
Encountering the Unsupported engine error while running npm install
typically occurs when you’re trying to install a package that requires a different version of Node.js or npm than what is currently installed on your system. To resolve this, update your Node.js or npm to the required version (>=v16) by checking your current version with node -v
or npm -v
in your terminal. Consider using NVM (Node Version Manager) to manage multiple Node.js versions on your system for seamless compatibility. Install NVM, run nvm install 16
, and then nvm use 16
.
When running composer install to install the HeadstartWP plugin, why do I get "./composer.json" does not match the expected JSON schema
?
If you encounter the error message “./composer.json” does not match the expected JSON schema when running composer install
to install the HeadstartWP plugin, the most probable reason is that you haven’t added the vendor prefix for the name property in your composer.json
file. Make sure that the name property follows the format of headstartwp/project-name
rather than just your-project-name
. Adding the appropriate vendor prefix should resolve the issue and allow the installation to proceed smoothly.
Why am I getting The lock file is not up to date with the latest changes in composer.json.
when running composer install for the HeadstartWP plugin?
If you encounter the error message The lock file is not up to date with the latest changes in composer.json while running composer install
for the HeadstartWP plugin, you can resolve it by running composer update
. This command ensures that the lock file is updated to match the latest changes in the composer.json
file. Running composer update
will synchronize the dependencies and bring everything up to date, allowing you to proceed with the installation smoothly.
Why is the HeadstartWP plugin getting installed in the vendor directory instead of the plugins directory?
This usually occurs if the composer/installers
package isn’t included in your composer.json
file. This package facilitates the customization of installation paths for specific package types, such as wordpress-plugin
.
Frequently Asked Questions
Alright, we’ve covered a lot of ground so far. You’ve done a fantastic job keeping up! But, I bet there are still a few questions buzzing around in your mind regarding this headless WordPress framework. Don’t worry, it’s completely normal.
At the moment, HeadstartWP and WPGraphQL aren’t on speaking terms. Although GraphQL is a fantastic tool for certain projects, it can sometimes add an extra layer of complexity that isn’t really necessary for most headless sites. Plus, it can also extend the time it takes to develop your project. That being said, the folks at 10up haven’t ruled out the possibility of integrating GraphQL in the future. But for now, their goal is to keep your stack simple, efficient, and effective.
Wrapping Up
So, are you ready to ride the wave of the headless revolution with HeadstartWP? With its rock-solid foundation powered by 10up, an unwavering commitment to simplicity, and a zest for exploring new frontiers, you’re all geared up to create the website you’ve always envisioned.
Now that you’ve mastered the art of setting up HeadstartWP, why not take your skills up a notch? Have a look at my comprehensive guide on how to integrate Storybook with HeadstartWP. It’s a great combo that works wonders together.
And if you’re all done with your project and itching to get it live, remember we’ve got you covered. Dive into my step-by-step guide to effortlessly deploy your HeadstartWP project to Vercel. Happy coding with HeadstartWP, my friend!
Share Your Thoughts