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.
WordPress (admin.yourdomain.com)
└── REST API / WPGraphQL
└── Next.js (yourdomain.com)
├── ISR pages (blog posts, pages)
└── SSR pages (search, user-specific)
// 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;
}
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 }}
/>
);
}
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
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.