Headless WordPress is transforming how developers build web experiences. By decoupling the frontend and backend, it offers superior flexibility, speed, and control. This guide walks you through creating a headless WordPress site using WPGraphQL and Next.js in 2025, helping you harness the full power of a modern web stack.
What is Headless WordPress?
In a traditional WordPress setup, your themes and plugins render the frontend through PHP. A headless approach decouples this entirely. WordPress acts only as a content management backend, while a separate frontend—like one built with React, Vue, or in our case, Next.js—renders the content.
Benefits:
- Performance: Static site generation (SSG) improves loading speed and scalability.
- Security: The public site doesn’t expose WordPress admin endpoints.
- Scalability: Easily integrate with microservices and APIs.
- Flexibility: Use modern tools and frameworks for UX.
Why WPGraphQL and Next.js?
WPGraphQL is a free plugin that adds a GraphQL server to WordPress, allowing structured data fetching.
Next.js is a React framework supporting SSR and SSG, essential for SEO and performance.
Together, they:
- Enable cleaner, faster API queries.
- Support rendering content statically or dynamically.
- Allow developers to use modern frontend architectures.
Prerequisites
- A local WordPress site (via LocalWP or similar).
- Node.js and npm installed.
- Basic knowledge of React and GraphQL.
Step 1: Set Up WordPress Backend
- Install WordPress locally.
- Go to Settings > Permalinks and set to “Post name”.
- Install and activate these plugins:
- WPGraphQL
- WPGraphQL for Advanced Custom Fields (if needed)
 
- Create some posts, pages, and optionally custom post types.
Step 2: Create Next.js App
- Initialize a new app:
npx create-next-app headless-wp
cd headless-wp
- Install dependencies:
npm install @apollo/client graphql
- Create .env.localand add:
NEXT_PUBLIC_WORDPRESS_API_URL=http://localhost:10003/graphql
Step 3: Set Up Apollo Client
Create a new file: lib/apollo.js
import { ApolloClient, InMemoryCache } from '@apollo/client';
const client = new ApolloClient({
  uri: process.env.NEXT_PUBLIC_WORDPRESS_API_URL,
  cache: new InMemoryCache()
});
export default client;
Step 4: Query Posts from WordPress
Create GraphQL query: lib/queries.js
import { gql } from '@apollo/client';
export const GET_POSTS = gql`
  query GetPosts {
    posts {
      nodes {
        id
        title
        slug
        excerpt
      }
    }
  }
`;
Create pages/index.js:
import client from '../lib/apollo';
import { GET_POSTS } from '../lib/queries';
export default function Home({ posts }) {
  return (
    <div>
      <h1>Recent Posts</h1>
      <ul>
        {posts.map(post => (
          <li key={post.id}>
            <a href={`/posts/${post.slug}`}>{post.title}</a>
          </li>
        ))}
      </ul>
    </div>
  );
}
export async function getStaticProps() {
  const { data } = await client.query({ query: GET_POSTS });
  return {
    props: { posts: data.posts.nodes },
    revalidate: 10
  };
}
Step 5: Create Dynamic Post Pages
- Create pages/posts/[slug].js
import client from '../../lib/apollo';
import { gql } from '@apollo/client';
const GET_POST = gql`
  query GetPost($slug: ID!) {
    post(id: $slug, idType: SLUG) {
      title
      content
    }
  }
`;
export default function Post({ post }) {
  return (
    <div>
      <h1>{post.title}</h1>
      <div dangerouslySetInnerHTML={{ __html: post.content }} />
    </div>
  );
}
export async function getStaticPaths() {
  const { data } = await client.query({
    query: gql`
      query AllSlugs {
        posts {
          nodes {
            slug
          }
        }
      }
    `
  });
  const paths = data.posts.nodes.map(post => ({ params: { slug: post.slug } }));
  return {
    paths,
    fallback: true
  };
}
export async function getStaticProps({ params }) {
  const { data } = await client.query({
    query: GET_POST,
    variables: { slug: params.slug }
  });
  return {
    props: {
      post: data.post
    },
    revalidate: 10
  };
}
Step 6: Add SEO Tags
Use next/head in each page:
import Head from 'next/head';
<Head>
  <title>{post.title}</title>
  <meta name="description" content={post.excerpt} />
</Head>
Step 7: Deploy to Vercel
- Push to GitHub.
- Go to Vercel and import the repo.
- Set your environment variable: NEXT_PUBLIC_WORDPRESS_API_URL
- Click deploy.
Optimization Tips
- Use Incremental Static Regeneration (ISR).
- Use CDN caching (Vercel does this automatically).
- Enable image optimization with next/image.
- Monitor performance with Google PageSpeed Insights.
Internal Resources
- Learn how to Speed Up WordPress Admin
- Fix WordPress White Screen of Death
- Check out Advanced WordPress Cleanup Services
External Resources
Conclusion
Building a headless WordPress site using WPGraphQL and Next.js empowers developers with flexibility, performance, and scalability. As more businesses demand lightning-fast experiences and modern UIs, this architecture becomes increasingly relevant. Follow this 2025 guide to create future-proof, maintainable, and high-performing WordPress sites.






 
			
         
			
         
			
         
			
         
			
         
			
         
			
         
			
         
			
         
			
         
			
         
			
        