Combine Headless WordPress with a React SPA (part 1)
Hi, I am Haris and will teach you how to create, manage and optimize a React SPA using a Headless WordPress backend through two blogs. In this first blog, I will explain the following: how we work with dynamic routing, create meta tags in a React SPA, how to install WordPress trough Docker, make use of ACF and how to use the Google Search Console. SSR is not needed in order to create a simple but manageable promotion website or blog. I will explain how we set up our basic WordPress environment on our local machine using Docker and making use of the integrated Rest API from WordPress. Next, we will learn how to make use of React Router and how to implement dynamic routing in our React environment. In part 2 I will discuss how we can expose our environment online for SEO.
WordPress is super simple to use, it’s very user-friendly, easy to update and has already done a lot of the work for us. To create a CMS from scratch can be very challenging and time-consuming. Since the 4.7 update in WordPress, we can make use of the WordPress REST API without making any modifications. The REST API will serve as our backend and will hold and manage the content for our website. In addition, tools like ACF (Advanced Custom Fields) can aid us in customizing our posts and pages. This way we can create a completely custom website trough a familiar platform like WordPress. Tools like Yoast SEO can help us with Search Engine Optimization and managing our indexation for Google’s Search Bot.
Why React SPA?
What is an SPA?
This guide will show examples using OSX, some terminal commands may be different between operating systems. If you have any questions on how to execute commands on your OS feel free to contact me.
In order to create a WordPress and React environment we have to install a couple of things. In this tutorial we will create an environment that is local for us. If you would like to host your set-up and learn more about DevOps check out this blog.
WordPress + Docker
Usually, WordPress requires an apache set up that will run the PHP for the WordPress environment. As a developer. we don’t want to manually configure everything. A great solution is to use Docker. We can run a local instance of a Docker container that will create our WordPress environment using compose. The WordPress installation can be different for Mac, Windows and Linux. You can find the docker installation guide here.
Once you have installed Docker on your local machine we can create our new WordPress site. There should be a basic understanding of how a terminal works in the next parts.
Let’s start by creating an empty folder where we will store the docker-compose.yml file.
mkdir wordpress-backend to create the WordPress-backend folder.
Navigate to the folder and create our docker-compose.yml file.
Next up we add the following code to it
These are the default settings for creating a WordPress environment. Now if you run docker-compose up docker will run in detached mode, pull the required Docker images and start the WordPress and database containers. You should see something like this:
Docker compose will host a local host environment that you can visit. Here you will be able to run the installation of WordPress (filling out the database might be different per install, you can leave it as default for now). You can set up the installation settings in any way you want, as this is not significant at this stage. Just make sure to uncheck the search engine visibility.
Now login to your website using the username and password you set. We don’t have to install anything else to access our Rest API since WordPress 4.7. In order to test if the API of our WordPress works for you, you can fire a get request in Postman or just visit the link. We can also check our Hello World post. You should be able to see the example page created by WordPress. Let’s create a couple of pages to simulate a real website.
You can now check all the pages available in the REST API.
you can always change the URL of your REST API using permalinks. In order to do so go to Settings > Permalinks . This will also change the URL of the REST API.
React + React Router
So now that we have our WordPress environment set up we can start working on the React part. React works with a Virtual DOM by manipulating the root div on our web page and allows a single page while the application changes or interacts with the user. That’s why React is a SPA. But we don’t want to be on one page, we would like to have multiple pages and navigate by changing the URL. This is where React Router will come in. React Router will serve as a navigation tool through our React SPA. It will simulate Routes by adjusting the URL and change the content on the page. Let’s start by creating a basic React App. For this, we will use NPM and Node. SO, create a new folder, outside the WordPress folder. Here we will create our app.
npx create-react-app wordpress-frontend
Then navigate to the folder and run it
cd wordpress-frontend && npm start
You should see the base create-react-app landing page.
Next, we will need some data before we actually start creating routes. Our routes will be based on the pages that exist in our WordPress environment. The first step will be to connect our frontend and backend to receive the content for us to display. In order to receive the pages, we have to GET the content from WordPress. We use the library Axios for this. Axios is basically the same as Fetch but has a couple of important added features. Some benefits of Axios:
Transformers: allow performing transforms on data before a request is made or after a response is received (We can use this later to only fetch differences to increase the performance
Interceptors: allow you to alter the request or response entirely (headers as well). Also, perform async operations before a request is made or before Promise settles. (This will be handy for spoofing 404 responses so pages that do not exist do not get indexed.)
Our content comes from the headless WordPress and will determine what pages/links/posts will be available. Luckily Axios allows us to work with promises what will come to a great benefit to us. First, we add Axios to our project with:
npm install axios
Then, let’s modify our App.js file in the src folder to reach out for the pages and display the page-slugs on our website.
The reason we use a class is to be able to use the constructor and add a pages state. We can then use the array that will be stored in this.state.pages to render our pages dynamically. The reason we use a pages state to store pages is so that we are able to store pages in the cookies or local storage of the user. Before we actually fetch the content we can then compare the local storage and our WordPress environment. We will discuss further optimization in the second blog.
Secondly, we will perform the function componentDidMount to get all of our pages. getAllPages will fetch the pages from our WordPress and store them into the state. In the render, we will perform a map of our state to render the slugs of our pages. You should see something like this.
Great! Now we can see that we get some data loaded into our website. Next, we will create individual pages, so we can visit them.
Let’s install react-router to create routing for React. npm install react-router-dom
React Router allows us to create individual routes for our pages and adjust our URL. In order to dynamically create the routing, we can add the following code. Don’t forget to import react-router-dom
Let’s change the following code inside the render function to:
Create a folder inside the src folder called components and add ExamplePage.js to it. Inside ExamplePage.js we will add the following code
This code allows us to use the pages from the state to create links and routes. As you can see when you click on one of the links you will be navigated to the right URL.
Let’s add some styling to our App like so.
Voilà, here we have a working example of our SPA with React Router trough headless WordPress. But there are some things missing. We still need to add our blog posts and 404 handling for the React Router. As you can see the pages will be switched instantly because we are not refreshing any page. This is one of the huge benefits or creating a website like this.
We can do the same thing with our blog posts. Next, we create a basic landing that page that will display our latest blogposts in a list.
Let’s start by creating a HomePage component. This will serve both as our landing page and homepage. Inside the components folder we can create a HomePage.js file. It will look something like this
Add styling to App.css
And finally, add the getAllPosts function to App.js
We basically do the same thing as we did for the Pages. A good practice is to combine the two fetches into one in order to avoid multiple requests. For sake of simplicity, the two functions are split here. Don’t forget to add the posts object to the state like so
Let’s implement our HomePage to the app. First off import the HomePage on App.js
Add HomePage<Link> to the Navbar
And add an extra route for the HomePage. We can also add a redirect to the default route.
As you can see the link to the homepage is "/" . This is the default route for React Router. We can make sure that the user will be redirected to the HomePage once a Route is not found. We do this by using the <Redirect> component. In order to use multiple routes like this, we have to wrap our routes in a <Switch> component. <Switch> basically allows us to “switch” between routes once a route is not found. See it more as a switch case but for our routes.
Our final App.js should look something like this:
So there we have it, dynamic routing trough WordPress and React Router.
You can always add more posts/pages if you want without adjusting the React code. If you got stuck somewhere check out the repository for this blogpost here.
In the next part of this series, we will discuss how we can add SEO and meta tags using React Helmet and Google Search Console. Feel free to comment with questions or contact me at firstname.lastname@example.org
I hope you enjoyed this tutorial and to see you back for part 2.