Library / SDK
9d8dev/next-wp avatar
9d8dev/next-wp

next-wp: a Next.js starter where WordPress redirects its own frontend away

Headless WordPress built with the Next.js App Router and React Server Components

1,301 stars328 forksTypeScriptMIT

At a glance

What is it?
next-wp is a headless WordPress starter on Next.js 16, React 19 and Tailwind v4, and the part worth understanding is the round trip: a small WordPress plugin calls your site when content changes so Next.js can revalidate, matched by a shared secret. Deploy on Railway and you get WordPress, MySQL and the frontend as three services in one click.
Who is it for?
next-wp fits a team that already publishes in WordPress and wants a fast React frontend without writing the data layer, because lib/wordpress.ts with its TypeScript definitions, server-side pagination and dynamic routes for posts, pages, authors, categories and tags are the parts you would otherwise write twice. Skip it if your content is not in WordPress, or if you need WooCommerce, since the README points at a separate next-woo project for that.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 78 days ago.
What is it written in?
Mainly TypeScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

WordPress becomes a CMS, not a website

The Railway template describes the shape in one sentence each: a MySQL database with a persistent volume, a WordPress service as the CMS, and Next.js as the frontend, with the CMS talking to the frontend rather than the other way round. Two WordPress side pieces make that work. The nextjs-headless theme redirects the WordPress frontend to your Next.js site, so an editor who lands on the CMS URL is sent to the public site instead of a half themed WordPress page. The next-revalidate plugin is pre-installed and auto-activated. There is also a separate headless theme project, repository 9d8dev/761.

Five commands to a running site

The quick start is a clone, an install and a dev server:

bash
git clone https://github.com/9d8dev/next-wp.git
cd next-wp
pnpm install
cp .env.example .env.local
pnpm dev

The site is then on localhost:3000. Prerequisites are Node.js 22.13 or later, pnpm 11 pinned through the packageManager field and enabled with corepack enable, and a WordPress site with the REST API enabled, which the README notes has been the default since WordPress 4.7. The dev script runs next dev with the turbo flag, and the package versions are pinned hard: next 16.3.0, react 19.2.4, tailwindcss 4.2.1, vitest 4.1.10.

Three environment variables, three different jobs

The .env.example file is short enough to read as documentation:

bash
WORDPRESS_URL="https://wordpress.com"
WORDPRESS_HOSTNAME="wordpress.com"
WORDPRESS_WEBHOOK_SECRET="your-secret-key-here"

WORDPRESS_URL is the full base URL used for API calls. WORDPRESS_HOSTNAME is only the domain, and its job is image rendering in Next.js, so media URLs can be optimised through the same host that serves them. The webhook secret is the shared credential for cache revalidation, and the example file suggests generating it with openssl rand -base64 32. Three variables with three distinct purposes is a good sign: there is no fourth variable doing something unnamed, and each is one you can explain in a code review.

Revalidation is a webhook with a secret on both ends

The feature that makes a headless setup usable is cache revalidation, and the implementation is deliberately small. The app directory contains an api/revalidate route, which is the Next.js side, and a plugin/ directory holds the WordPress plugin, which is the other side. When an editor publishes or updates something in WordPress, the plugin calls the Next.js route with the secret; the route compares it against WORDPRESS_WEBHOOK_SECRET and revalidates. After deploying on Railway you configure this by hand: open WordPress Admin, go to Settings and then Next.js Revalidation, enter your Next.js URL, copy the WORDPRESS_WEBHOOK_SECRET value out of your Next.js service variables, paste it into the plugin settings and save. The README's test procedure is the right one to follow literally, since it verifies both directions: publish a post, see it on the Next.js site, edit it in WordPress, refresh, and watch the change appear.

The App Router tree is where most of the work is done

The documented project structure is an honest map. Under app/ you get api/og for social card generation and api/revalidate for the webhook, then dynamic routes for pages at pages/[slug], posts at posts/[slug], and archives for authors, categories and tags, plus layout.tsx, the homepage page.tsx and a dynamic sitemap.ts. The data layer is two files: lib/wordpress.ts holding the WordPress API functions and lib/wordpress.d.ts holding the TypeScript definitions, which is what the type-safe WordPress API feature means in practice. Components are split by concern, posts/ with post-card.tsx, filter.tsx and search-input.tsx, nav/, theme/ and ui/ for shadcn/ui. Feature level items include server-side pagination for large content libraries, search with debouncing, a dynamic sitemap, OG image generation and dark mode via next-themes. Configuration surfaces you will actually edit are menu.config.ts and site.config.ts.

The Dockerfile needs WordPress at build time

The multi stage Dockerfile explains a constraint you would otherwise hit at deploy time. The builder stage declares ARG WORDPRESS_URL and ARG WORDPRESS_HOSTNAME and promotes them to ENV before running pnpm build, which means the WordPress URL is baked into the build output rather than read at runtime. Two more details are worth copying into your own projects. The comment at the top says Node 22.13 or newer is required because the pinned pnpm depends on the builtin node:sqlite module, and corepack is enabled in each stage so the pinned version resolves from package.json. The runner stage copies the standalone build, creates a non root user with uid 1001, runs as that user, sets HOSTNAME to 0.0.0.0 because Railway sets PORT dynamically, exposes 3000 and starts node server.js.

Railway versus Vercel decides who owns the CMS

Two deployment paths are documented and they are not equivalent. Railway is described as recommended because it deploys the complete stack with one click, using a custom WordPress Docker image published at ghcr.io/9d8dev/next-wp-wordpress with the revalidation plugin, the headless theme, WP-CLI for automated setup, MySQL 8.0 with a persistent volume and the Next.js app, and you then finish the WordPress install wizard and configure the plugin. The Vercel button, by contrast, asks for the same three environment variables, which means you supply your own WordPress somewhere else. The repository has both vercel.json and railway.json plus a Dockerfile and a wordpress/ directory, so the supported paths are clear. A WooCommerce version is a separate project, next-woo, which is the fork in the road if you sell anything.

Against hand writing the data layer yourself

The alternative is to start an empty Next.js app, write the fetch functions for the WordPress REST API, add types for the response shapes, and build the routes for posts, pages, archives, search, sitemap and social cards. and the parts it has done are the boring parts: server-side pagination, search input with debouncing so you are not firing a query per keystroke, a sitemap that reflects content as it changes rather than as it was at build time. What you get in exchange is a codebase you now own and have to keep current, with Next.js 16 and React 19 pinned, a plugin whose behaviour you depend on for freshness, and a layout you did not design. For a marketing site or a content property that is a good trade; for a heavily customised product it is a starting point and nothing more.

Editorial conclusion

next-wp fits a team that already publishes in WordPress and wants a fast React frontend without writing the data layer, because lib/wordpress.ts with its TypeScript definitions, server-side pagination and dynamic routes for posts, pages, authors, categories and tags are the parts you would otherwise write twice. Skip it if your content is not in WordPress, or if you need WooCommerce, since the README points at a separate next-woo project for that. Before deploying, set WORDPRESS_URL, WORDPRESS_HOSTNAME and WORDPRESS_WEBHOOK_SECRET and then paste the same secret into the WordPress plugin settings, because a mismatch is the documented cause of revalidation silently not working, and test it by publishing a post and editing it. The last push was on 2026-08-06 and the project is MIT licensed with pnpm 11 and Node 22.13 required.

Frequently asked questions

How do I run next-wp locally?

Clone the repository, run pnpm install, copy .env.example to .env.local and edit it with your WordPress URL, then run pnpm dev. The site is served at localhost:3000. Node.js 22.13 or newer and pnpm 11 enabled through corepack are required.

How does cache revalidation work between WordPress and Next.js?

The next-revalidate WordPress plugin calls the Next.js api/revalidate route when content changes, and the route matches the request against WORDPRESS_WEBHOOK_SECRET. After deployment you enter your Next.js URL and the same secret in WordPress Admin under Settings and Next.js Revalidation.

What does the Railway template include?

It deploys three services: MySQL with a persistent volume, WordPress using the custom image ghcr.io/9d8dev/next-wp-wordpress with the next-revalidate plugin and the nextjs-headless theme pre-installed, and the Next.js frontend. WP-CLI handles automated WordPress setup.

Official sources

  1. 9d8dev/next-wp on GitHub
  2. License: MIT
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/9d8dev-next-wp.svg)](https://hysenlabs.com/projects/9d8dev-next-wp)

Was this useful?