Learn how to use WordPress as a headless CMS with Next.js, using server-side rendering (SSR) for fast, SEO-friendly, and highly scalable front-end applications.

Overview: Why Use Headless WordPress with Next.js?

Headless WordPress separates content management (WordPress) from presentation (Next.js). WordPress exposes content via its REST API or GraphQL, while Next.js renders the front end using React and server-side rendering (SSR).

This approach is ideal when you want:

  • Modern React-based front-end experiences
  • Better performance and caching control
  • Improved SEO through SSR and clean URLs
  • Scalability across multiple channels (web, apps, kiosks)

Key Concepts: Headless, SSR, and the WordPress API

Headless WordPress

In a headless setup, WordPress still stores posts, pages, media, and custom fields, but it no longer renders the public-facing theme. Instead, it exposes content via APIs that your Next.js app consumes.

Common API options:

  • WordPress REST API – built-in, JSON-based endpoints like /wp-json/wp/v2/posts
  • GraphQL (via a plugin) – a single endpoint with flexible queries

Server-Side Rendering (SSR) in Next.js

SSR means pages are rendered on the server at request time, then sent as fully formed HTML to the browser. This is excellent for SEO and dynamic content that changes frequently.

In Next.js, SSR is typically implemented using getServerSideProps in the pages directory or the app directory’s server components and data fetching functions.

Prerequisites

  • A working WordPress site (can be staging or local)
  • Node.js and npm or yarn installed on your machine
  • Basic familiarity with React and JavaScript
  • Access to your WordPress admin dashboard

Step 1: Prepare WordPress as a Headless CMS

1.1 Enable the REST API (Default)

Modern WordPress includes the REST API by default. To verify it is available:

  1. Open a browser and go to https://your-site.com/wp-json/.
  2. You should see a JSON response with information about your site and available routes.

1.2 Configure Permalinks

Clean permalinks make your content easier to work with and better for SEO.

  1. Log in to Dashboard ? Settings ? Permalinks.
  2. Select Post name or a custom structure that includes %postname%.
  3. Click Save Changes.

1.3 Create Content to Test

Create a few posts and pages so your Next.js app has data to display.

  1. Go to Dashboard ? Posts ? Add New.
  2. Add a title, body content, and a featured image.
  3. Click Publish.

Step 2: Set Up a New Next.js Project

2.1 Create the Project

From your terminal, run:

npx create-next-app@latest headless-wp-next
cd headless-wp-next

2.2 Choose TypeScript (Optional)

During setup, you can choose TypeScript for better type safety, especially useful when working with API responses from WordPress.

2.3 Configure Environment Variables

Create a .env.local file in your project root:

WORDPRESS_API_URL=https://your-site.com/wp-json/wp/v2

Restart your dev server after adding or changing environment variables.

Step 3: Fetch WordPress Content with SSR

3.1 Basic SSR Page Using getServerSideProps

In the pages directory, create pages/index.js (or .tsx):

export async function getServerSideProps() {
  const res = await fetch(`${process.env.WORDPRESS_API_URL}/posts?per_page=5`);
  const posts = await res.json();

  return {
    props: {
      posts,
    },
  };
}

export default function Home({ posts }) {
  return (
    <main>
      <h1>Latest Posts</h1>
      <ul>
        {posts.map((post) => (
          <li key={post.id}>
            <a href={`/posts/${post.slug}`}>
              {post.title.rendered}
            </a>
          </li>
        ))}
      </ul>
    </main>
  );
}

3.2 What You Should See

When you run npm run dev and visit http://localhost:3000, you should see:

  • A heading like “Latest Posts”
  • A list of post titles pulled from your WordPress site
  • Each title linking to a URL like /posts/your-post-slug

Step 4: Create Dynamic Post Pages with SSR

4.1 Dynamic Route for Posts

Create a file at pages/posts/[slug].js:

export async function getServerSideProps({ params }) {
  const { slug } = params;
  const res = await fetch(
    `${process.env.WORDPRESS_API_URL}/posts?slug=${slug}`
  );
  const data = await res.json();

  if (!data.length) {
    return {
      notFound: true,
    };
  }

  return {
    props: {
      post: data[0],
    },
  };
}

export default function Post({ post }) {
  return (
    <article>
      <h1>{post.title.rendered}</h1>
      <div
        dangerouslySetInnerHTML={{ __html: post.content.rendered }}
      />
    </article>
  );
}

4.2 What You Should See

Clicking a post title on the homepage should now:

  • Navigate to a URL like /posts/example-post
  • Render the full post title and content from WordPress
  • Show HTML formatting (headings, lists, images) as in the WordPress editor

Step 5: Handling SEO with SSR

5.1 Dynamic Meta Tags

Use the Next.js Head component to set meta tags based on WordPress data:

import Head from 'next/head';

export default function Post({ post }) {
  const title = post.yoast_head_json?.title || post.title.rendered;
  const description = post.yoast_head_json?.description || '';

  return (
    <>
      <Head>
        <title>{title}</title>
        {description && (
          <meta name="description" content={description} />
        )}
      </Head>
      <article>
        <h1>{post.title.rendered}</h1>
        <div
          dangerouslySetInnerHTML={{ __html: post.content.rendered }}
        />
      </article>
    </>
  );
}

5.2 Canonical URLs and Open Graph

If you use an SEO plugin in WordPress, many meta fields are already available in the REST API. You can map them into <meta> tags in Head for canonical URLs, Open Graph, and Twitter cards.

Step 6: Authentication and Protected Content

For public content, anonymous REST API access is enough. For protected content (e.g., members-only posts), you will need authentication.

  • Cookie-based auth – for same-domain setups
  • JWT or application passwords – for decoupled front-ends

In SSR, you can read cookies or headers in getServerSideProps, validate the user, and then call the WordPress API with appropriate credentials.

Step 7: Performance, Caching, and Deployment

7.1 Caching Strategies

Because SSR runs on every request, caching is critical:

  • Use HTTP caching headers from your Next.js responses
  • Leverage a CDN or hosting platform edge cache
  • Consider hybrid rendering (SSR for some routes, static generation for others)

7.2 Deploying Your Next.js App

When deploying, make sure:

  • WORDPRESS_API_URL is set in your production environment
  • Your WordPress site is publicly reachable from the Next.js server
  • HTTPS is enabled on both WordPress and your front-end domain

How This Fits with Your Existing WordPress Workflow

Your content editors can continue using WordPress as usual:

  • Create and edit posts via Dashboard ? Posts
  • Manage media via Dashboard ? Media
  • Use custom fields or custom post types as needed

The difference is that instead of a traditional theme, your Next.js application consumes the content and renders it with React, giving you more control over performance, design systems, and multi-channel publishing.

Troubleshooting: Common Issues

  • 404 on /wp-json/ – Check that permalinks are set correctly and no security plugin is blocking the REST API.
  • CORS errors – Configure CORS headers on your WordPress host to allow requests from your Next.js domain.
  • Slow responses – Add caching or a performance plugin on WordPress, and consider limiting per_page in API calls.

Next Steps

  • Introduce a design system or component library in your Next.js app
  • Use incremental static regeneration (ISR) for less frequently updated content
  • Explore GraphQL for more efficient and flexible queries

Video

Leave a Reply

readers also liked

Need Help With Your Website?

If you’re reading this because you’re planning a website—or trying to improve one—you don’t have to guess your way through it.

I offer a free 30-minute consultation where we’ll talk through your goals, your budget, and the most efficient way to get a professional website online.

Whether you need full website design, help choosing the right platform, guidance on hosting, or a clear plan you can execute yourself, I’ll give you direct, practical advice tailored to your situation.

Even if you don’t move forward with my services, you’ll leave the call knowing exactly what your next step should be.

Give us a call at
(208) 449-4466

Or give us your info and we will call you.

Give us a call at (208) 449-4466
Or give us your info and we will call you.

Get a Quote/Contact Form
By submitting this form, you acknowledge that you have read and agree to our Privacy Policy and Terms & Conditions.

Report an Issue

Flag incorrect info, broken media, or unclear steps. we review every report.

You’re reporting: {Post Title}

Content Report

By submitting this form, you acknowledge that you have read and agree to our Privacy Policy and Terms & Conditions.

Request a New Topic

Suggest a tutorial, guide, or course idea you’d like to see added. I review every submission.

Topic Request (Knowledge Base)

By submitting this form, you acknowledge that you have read and agree to our Privacy Policy and Terms & Conditions.

Websites That Work as Hard as You Do

Are you ready to grow your business?
Call (208) 449-4466 or schedule an in-person meeting today.