Building a React App Using WordPress as a Headless CMS
Máximo Martinez Soria
WordPress
We are increasingly used to seeing complex, dynamic, and reactive interfaces.
CMS platforms like WordPress make building these types of interfaces a bit harder with the tools they provide by default.
This led to the emergence of the Headless CMS concept. In other words, using only the CMS content management tools and consuming an API to gain full freedom over the presentation layer. In addition, new CMS platforms were created specifically to be headless. Some examples are Strapi and Keystone, among others.
In this article, we’ll use WordPress as a Headless CMS and consume its API from a React application.
Prerequisites
One thing we won’t cover is installing WordPress.
That said, all we need is a WordPress installation with a few posts. You can use the FakerPress plugin to generate dummy data.
Create a React project
The React ecosystem is constantly evolving, and there are already many tools that simplify things like Server-Side Rendering or Static Site Generation. Next.js and Gatsby are among the most popular.
For this example, we won’t use any of those tools, but I recommend giving them a try. They can save you a lot of time.
Let’s use create-react-app to generate the scaffold and everything needed to get React up and running.
# Create project
yarn create react-app blog
# Run project
cd blog
yarn start
Once the project is running, we’ll clean up the App.js file a bit.
import './App.css';
function App() {
return <div className="App"></div>;
}
export default App;
Now we’re ready to get started.
Consume the WordPress API
By default, WordPress provides a REST API that we can use to fetch data. If needed, we can also create custom endpoints to modify the logic.
The endpoint to fetch all posts is /wp-json/wp/v2/posts.
We’ll use the Fetch API to make a GET request to that endpoint.
// In my case, WordPress is running at http://blog.test/
// GET request
fetch('http://blog.test/wp-json/wp/v2/posts')
// Convert the response to JSON
.then((response) => response.json())
// Receive the posts
.then((posts) => {
console.log(posts);
});
We’ll move this code into a useEffect hook to tell React to run it when the component mounts. We’ll also create two state variables. One to store the posts and another to track whether the request is loading.
import { useEffect, useState } from 'react';
import './App.css';
function App() {
const [posts, setPosts] = useState([]);
const [isLoading, setIsLoading] = useState(false);
/* useEffect allows us to run a function whenever one of its
dependencies changes. The dependencies are defined in the array
passed as the second argument.
In this case, we only want it to run once, so we leave the array empty.
*/
useEffect(() => {
setIsLoading(true);
fetch('http://blog.test/wp-json/wp/v2/posts')
.then((response) => response.json())
.then((posts) => {
setPosts(posts);
setIsLoading(false);
});
}, []);
return <div className="App"></div>;
}
export default App;
Display the posts
Finally, we’ll display the posts on the screen. For this example, we’ll simply show the title and content, but there’s much more information available.
import { useEffect, useState } from 'react';
import './App.css';
function App() {
const [posts, setPosts] = useState([]);
const [isLoading, setIsLoading] = useState(false);
useEffect(() => {
setIsLoading(true);
fetch('http://blog.test/wp-json/wp/v2/posts')
.then((response) => response.json())
.then((posts) => {
setPosts(posts);
setIsLoading(false);
});
}, []);
if (isLoading) {
return <div>Loading...</div>;
}
return (
<div className="App">
<h1>Posts</h1>
<ul>
{posts.map((post) => (
<li key={post.id}>
<article>
<h2>{post.title.rendered}</h2>
<div
dangerouslySetInnerHTML={{ __html: post.content.rendered }}
></div>
</article>
</li>
))}
</ul>
</div>
);
}
export default App;
Improvements
It’s working. We’re using the WordPress dashboard to create content and the power of React to build interfaces. Best of both worlds.
This is a simple example that could use several improvements. Here are a few ideas you can implement:
- Add a
catchto the request and handle errors properly. - Extract the
articleand its contents into aPostCardcomponent. - Add pagination or lazy loading. The WordPress API allows you to pass a
pageparameter to handle this. - Add styles.
Leave a link to your repository in the comments so we can see how it turned out.
Aug 23, 2025
WordPress
How the Model Context Protocol (MCP) Will Transform Martech and WordPress
Apr 8, 2026
WordPress
How CMS drives digital transformation and marketing agility
Feb 11, 2026
WordPress