Building a React App Using WordPress as a Headless CMS

Máximo Martinez Soria

Jul 7, 2022 | 4 min to read |

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 catch to the request and handle errors properly.
  • Extract the article and its contents into a PostCard component.
  • Add pagination or lazy loading. The WordPress API allows you to pass a page parameter to handle this.
  • Add styles.

Leave a link to your repository in the comments so we can see how it turned out.

Author

Máximo Martinez Soria