WordPress

Headless WordPress with Next.js: A Production Guide

dev.prakah2011 April 20, 2025 3 min read
🌐

Headless WordPress decouples the CMS from the presentation layer — editors keep the familiar WordPress admin, while your frontend is a blazing-fast Next.js application. This architecture delivers the best of both worlds: editorial flexibility and modern frontend performance.

Why Go Headless?

  • Performance: Next.js with ISR (Incremental Static Regeneration) serves pre-built HTML — no PHP execution per request
  • Developer freedom: Use any CSS framework, component library, or animation tool
  • Multi-channel: The same WordPress REST API can feed a website, mobile app, and digital signage simultaneously
  • Security: The WordPress admin is never publicly exposed

The Architecture

WordPress (admin.yourdomain.com)
  └── REST API / WPGraphQL
        └── Next.js (yourdomain.com)
              ├── ISR pages (blog posts, pages)
              └── SSR pages (search, user-specific)

Fetching Posts with the REST API

// lib/wordpress.ts
const WP_API = process.env.WP_API_URL; // e.g. https://admin.yourdomain.com/wp-json/wp/v2

export async function getPosts(page = 1) {
  const res = await fetch(
    `${WP_API}/posts?_embed&per_page=12&page=${page}`,
    { next: { revalidate: 60 } } // ISR: revalidate every 60 seconds
  );
  if (!res.ok) throw new Error('Failed to fetch posts');
  return res.json();
}

export async function getPostBySlug(slug: string) {
  const res = await fetch(
    `${WP_API}/posts?slug=${slug}&_embed`,
    { next: { revalidate: 60 } }
  );
  const posts = await res.json();
  return posts[0] ?? null;
}

Rendering Post Content Safely

WordPress returns HTML content in post.content.rendered. Use dangerouslySetInnerHTML carefully — only render content from your own trusted WordPress instance.

export default function PostContent({ html }: { html: string }) {
  return (
    <article
      className="prose prose-invert max-w-none"
      dangerouslySetInnerHTML={{ __html: html }}
    />
  );
}

Incremental Static Regeneration (ISR)

ISR is the killer feature for headless WordPress. Pages are statically generated at build time and silently re-generated in the background when content changes — visitors always get a fast cached response.

// app/blog/[slug]/page.tsx
export async function generateStaticParams() {
  const posts = await getPosts();
  return posts.map(post => ({ slug: post.slug }));
}

export const revalidate = 60; // re-generate after 60s if requested

Setting Up Revalidation Webhooks

Install the WP Webhooks plugin or write a custom plugin that fires a request to your Next.js revalidation API endpoint whenever a post is published or updated. This triggers on-demand ISR so your site updates within seconds of publishing.

// app/api/revalidate/route.ts
export async function POST(req: Request) {
  const { secret, slug } = await req.json();
  if (secret !== process.env.REVALIDATE_SECRET) {
    return Response.json({ error: 'Unauthorized' }, { status: 401 });
  }
  revalidatePath(`/blog/${slug}`);
  return Response.json({ revalidated: true });
}

Headless WordPress with Next.js is production-proven and scales to millions of requests without touching your WordPress server. Start with ISR, add on-demand revalidation, and you’ll have a setup that outperforms any traditional WordPress stack.

dev.prakah2011
dev.prakah2011

Developer & author at DevForge Agency.

Related Articles

🌐
WordPress

WordPress Security Hardening: 15 Must-Do Steps