WordPressApril 12, 2026

Building a Headless WordPress Site with Next.js App Router: What I Learned

I recently built my first production headless WordPress project using the Next.js App Router. Here's an honest account of what worked well, what didn't, and whether I'd do it again.

Topics covered

  • Headless WordPress setup
  • WPGraphQL + App Router
  • Static generation with ISR
  • When headless is and isn't worth it

Why I Decided to Go Headless

The project was a content-heavy marketing site with high traffic expectations and a client who needed editorial flexibility. WordPress's content management is unmatched, but the front end needed to be fast — genuinely fast, not 'caching plugin' fast. Decoupling the front end via Next.js App Router with static generation felt like the right call. What followed was one of the most instructive builds I've done in years.

Setting Up WPGraphQL

WPGraphQL is the go-to plugin for exposing WordPress content as a GraphQL API, and it's excellent. Installation is straightforward, and it immediately exposes posts, pages, custom post types, taxonomies, and ACF fields (via WPGraphQL for ACF) through a clean, typed API. The interactive GraphiQL explorer built into the WordPress admin is genuinely useful for building and testing queries before writing a line of front-end code. My advice: plan your content model and ACF field groups before writing any GraphQL queries — retrofitting queries to a changing schema is painful.

Fetching Data in the App Router

The App Router's server components make data fetching cleaner than the Pages Router ever was. Each page component fetches its own data directly — no getStaticProps, no prop drilling. For static pages, I used fetch with Next.js's built-in caching and revalidation. For the blog listing, generateStaticParams handles all post slugs at build time, and I set a revalidation interval for ISR so new posts published in WordPress appear on the site within minutes without a full rebuild. The combination of static generation and ISR is genuinely elegant once you understand the caching model.

The Revalidation Challenge

The trickiest part of any headless WordPress setup is cache invalidation — making the front end reflect content changes quickly without rebuilding the entire site. I implemented a WordPress webhook (fired on post publish and update) that triggers Next.js's on-demand revalidation endpoint, invalidating only the affected pages. This means a new blog post goes live on the front end within seconds of being published in WordPress. Setting this up correctly — handling authentication on the revalidation endpoint, ensuring webhooks fire reliably — took longer than any other part of the project.

What Worked Well

Performance was the big win. The finished site scores consistently above 95 on Lighthouse for both mobile and desktop, with LCP well under 1.5 seconds. The separation of concerns is also genuinely nice: content editors work in a familiar WordPress admin, and front-end changes don't require touching WordPress at all. TypeScript type generation from the GraphQL schema (via GraphQL Code Generator) meant the front end was fully typed — catching data shape mismatches at build time rather than at runtime.

What I'd Do Differently

Headless WordPress adds real complexity, and it's not always the right choice. For this project, the performance requirements justified it. But for most small-to-medium WordPress sites, a well-optimised traditional WordPress build on good hosting will perform adequately and be significantly simpler to maintain. The editorial experience is also degraded in headless setups — previewing unpublished content requires extra work, and clients lose the live preview functionality they're used to. Go headless when the performance or architecture requirements genuinely demand it, not because it's interesting.

Written by Manan Vyas

Senior WordPress Developer · Manchester, UK

Get in Touch