2026-01-24
Next.js vs WordPress in 2025: a decision tree and the headless path
Binary questions to pick stack: WordPress monolith, React app (Next.js), or headless WordPress. Example: loading posts from the WP REST API in the Next.js App Router.
Problem you solve: you need to decide where the CMS lives and where the frontend lives without star-rating tables or slogans.
What you get from this article: a binary decision tree and, if the hybrid wins, a concrete snippet to read posts from WordPress in Next.js (App Router) via the REST API.
How you know the choice is coherent: each branch ends with a checkable constraint (team, editorial budget, hosting).
Note: “Next.js” here means “React app with SSR/SSG”; the same REST pattern applies to Nuxt with
useFetch/$fetchagainstwp-json.
Decision tree (answer in order)
1. Who updates content weekly?
- Mostly non-technical editors wanting WYSIWYG + plugins → favour classic WordPress (monolith) or headless WP if the UI must be custom.
- Developers only (Markdown, Git, PR review) → you often do not need WordPress: SSG, Nuxt Content, etc. reduce surface area.
2. Do you need user login, cart, dashboards, or complex app logic in the frontend?
- Yes → Next.js (or Nuxt) as the app; WordPress at most as a headless CMS, not “theme + 30 plugins”.
- No, it is a brochure/blog → WordPress monolith can be enough if hosting + theme are disciplined.
3. Is the main constraint time-to-market on a tight budget?
- Yes → WordPress + solid theme + few plugins usually beats a pure custom marketing MVP.
- No, the constraint is LCP/INP on a critical funnel and full markup control → custom frontend (Next/Nuxt) + headless CMS or repo-based content.
4. Must you integrate internal APIs, feature flags, server-side A/B, multi-tenant?
- Yes → Next.js/Nuxt (or another app framework); WordPress monolith becomes friction.
- No → stay on monolith until you measure a real bottleneck.
Three likely outcomes
| Outcome | When | Risks to manage |
|---|---|---|
| WordPress monolith | Frequent content, editorial team, mostly static site | Performance (theme/plugins), core updates, security |
| Next.js / Nuxt without WP | Content in Git or a headless SaaS (Sanity, Contentful, …) | Non-technical editor UX |
| Headless WordPress | Editors want WP, frontend wants controlled React/Vue | Two deploys, caching, REST/GraphQL auth, hosting cost |
Headless WordPress + Next.js: minimal pattern
WordPress exposes the REST API at /wp-json/ (see REST API Handbook).
Example Server Component (App Router) fetching latest posts (sanitize HTML before rendering content.rendered in production):
// app/blog/page.tsx — set WP_URL in .env
const WP = process.env.WP_URL ?? 'https://example.com'
type WpPost = { id: number; slug: string; title: { rendered: string } }
export default async function BlogIndex() {
const res = await fetch(`${WP}/wp-json/wp/v2/posts?per_page=5&_fields=id,slug,title`, {
next: { revalidate: 60 },
})
if (!res.ok) throw new Error(`WP ${res.status}`)
const posts = (await res.json()) as WpPost[]
return (
<ul>
{posts.map((p) => (
<li key={p.id}>
<a href={`/blog/${p.slug}`}>{p.title.rendered}</a>
</li>
))}
</ul>
)
}
Headless checklist:
- Canonical URLs and CORS if the browser calls WP directly (prefer server-to-server as above).
- Auth for draft previews: Application Passwords or a JWT plugin — never ship admin creds to the client.
- ISR / revalidate in Next or Nuxt route rules so you do not hammer WP on every request.
Nuxt 3 analogue
Same fetch on the server:
const WP = useRuntimeConfig().public.wpUrl as string
const { data: posts } = await useFetch(`${WP}/wp-json/wp/v2/posts`, {
query: { per_page: 5, _fields: 'id,slug,title' },
})
Summary
- Do not pick Next.js “because it is modern” if the team cannot maintain it.
- Do not use WordPress as an app framework if business logic belongs in the frontend.
- If you need WP editors + a controlled frontend, headless is the clean third way — the code above is a feasibility spike in under an hour.
Final check: can you name who deploys WP, who deploys the frontend, and where HTML is cached? If both sides are clear, the stack is defined.