WordPress is a free, open-source tool used to create beautiful and efficient websites — either for business or personal purposes. To be more precise, it’s a content management system (CMS) written in PHP, which uses MySQL as its database. WordPress is big, and according to the official WordPress website, “43% of the web is built on WordPress.”
One of the many fantastic things that WordPress gives is the Rest API. It enables developers to produce exciting plugins and themes and gives them access to the WordPress CMS to power third-party applications.
WPGraphQL is a free, open-source WordPress plugin that adds a GraphQL API to your WordPress site.
In this article, we are going to learn how to build a headless WordPress app with React and WPGraphQL.
While You Are Here, Why Not Learn More About How To Start Developing Plugins for WordPress?
Prerequisites
- Code Editor
- Basic knowledge of React and GraphQL
What Is Headless CMS?
A headless CMS is a type of content management system (CMS) that simply offers backend functionalities, allowing content to be accessed via a GraphQL or REST API and rendered on any device.
The backend (creation and storage) and front end (design and deployment) of a headless CMS are entirely separated. The frontend presentation layer management is not handled by a Headless CMS. The JAMstack model, which combines JavaScript, APIs, and Markup to simplify web development and improve user experiences, is another scenario where headless CMS is particularly well suited.
It can also be defined as a content management system (CMS) that maintains and organizes content without a front-end or display layer. Your content and assets are stored in the headless CMS. Then, you distribute that content via a content API to all the places you need it, including your website, your mobile app, etc.
What Is Headless WordPress?
A headless WordPress site is one that uses WordPress for content management and a different, personalized frontend stack such as React to show content to site visitors. Although a site created using headless WordPress has numerous advantages, one of its main benefits is the decoupling of content editing teams and developers.
By using a Headless WordPress, a marketing team and content team can keep using their familiar WordPress interface, while the development team can continue using their favorite tools, like React and GraphQL.
Benefits of Headless WordPress
- Front-end Flexibility: WordPress’ extensive library of customizable themes and plugins gives you a lot of front-end flexibility. However, not everyone may want to have this kind of front-end control over their website. You can keep the “content management” portion of the WordPress CMS that you already know and love by using a headless CMS, and you can outsource your front end to almost any other program you like as long as it can communicate with the WordPress API.
- Improved site loading time: The logic of showing data is moved to the client-side due to the separation of the presentation layers, which significantly speeds up data transfer, and improves site loading time. This means that with headless WordPress, you do not have to worry about your site taking too long to respond.
- Improved Security: the headless WordPress essentially offers a free additional security layer. In essence, you’re creating a secret, difficult-to-find server. How come? Headless WordPress doesn’t have a login screen right there on the front-end server; it only connects to your websites and apps via an API. As a result, it will be considerably more difficult for hackers to access your explicit content.
Starter Project(Demo)
For our project, we will be using code sandbox. To get started with our starter project on code sandbox, click here. Once you have opened up our code sandbox starter project, the interface should be pretty familiar to you. This is what our directory looks like below:
The directory contains all the files for our demo project, and it can be explored, as often as wanted.
This is what our starter project looks like below:
source: Codesandbox
Before we get started on how to connect this application to WordPress, let’s look at some of the files that make up the application:
//App.js
import React from "react";
import { Route, Switch } from "react-router-dom";
import HomePage from "./pages/HomePage";
import PostPage from "./pages/PostPage";
import "./styles.css";
export default function App() {
return (
<Switch>
<Route exact path="/" component={HomePage} />
<Route path="/blog/:slug" component={PostPage} />
</Switch>
);
}
The App.js file is the root of our application. We first imported some packages from react-router to do our routing. Then we have the Homepage and PostPage files, and some styles with them. In our export default function, we have our routing where we are looking at the particular paths that are in the browser, and then rendering or displaying particular components based on that path.
This forms the basis of our navigation between our post, our home page which displays a list of posts, and our post page which displays post details.
//HomePage.js
import React from "react";
import PostsList from "../components/PostsList";
export default function HomePage() {
return (
<div className="page-container">
<h1>Blog</h1>
<PostsList />
</div>
);
}
In our HomePage.js you can see that it uses another component called PostList. Let’s look at our PostList.js component:
//PostList.js
import React from "react";
import PostCard from "../components/PostCard";
import { data } from "../dummy-data/posts";
export default function PostsList() {
const loading = false;
const error = null;
if (loading) return <p>Loading posts...</p>;
if (error) return <p>Error :(</p>;
const postsFound = Boolean(data?.posts.nodes.length);
if (!postsFound) {
return <p>No matching posts found.</p>;
}
return (
<div className="posts-list">
{data.posts.nodes.map((post) => (
<PostCard key={post.databaseId} post={post} />
))}
</div>
);
}
The PostList.js is the component that fetches our data to display our list. We did that from the dummy-data/posts, where we have an export that gives us a JSON object back in a particular shape, and this is the shape of the file or data that we would expect back from GraphQL.
Create an Apollo Client
In our PostList.js, the first thing we want to do to make our application dynamic is to replace some of the codes with code that will call out to a GraphQL server.
There are a few different methods for interacting with GraphQL servers, but the method we are going to use in this tutorial will involve a package called apollo client, and then we will implement an apollo provider pattern to wrap our application inside of code that can provide us access to apollo client so that we can use the useQuery hook throughout our React components.
To start this process, the first thing we need to do is head back into our src folder, create another folder inside, and call it lib folder. Inside our lib folder, we will create a file named called apollo.js, where we are going to include the following code:
//apollo.js
import { ApolloClient, InMemoryCache } from "@apollo/client";
const client = new ApolloClient({
uri: `https://demo.wpgraphql.com/graphql`,
cache: new InMemoryCache(),
});
export default client;
Here, we imported the ApolloClient and InMemoryCache from @apollo/client, then we created a new variable called client. The uri is the URL to the graphql server that we are going to be querying, then we have our cache property.
Implement the Apollo Provider Component
Now that we have configured our apollo client, what we want to do now is implement an apollo provider component at the root of our application.
What this allows us to do is to use the useQuery hook throughout our React component, so that we can do our data fetching inside of each component. To accomplish that, we first have to head into our app.js file, and edit it like this:
//App.js
import React from "react";
import { Route, Switch } from "react-router-dom";
import { ApolloProvider } from "@apollo/client";
import client from "./lib/apollo";
import HomePage from "./pages/HomePage";
import PostPage from "./pages/PostPage";
import "./styles.css";
export default function App() {
return (
<ApolloProvider client={client}>
<Switch>
<Route exact path="/" component={HomePage} />
<Route path="/blog/:slug" component={PostPage} />
</Switch>
</ApolloProvider>
);
}
The first thing we did was import ApolloProvider from @apollo/client, then we imported the client that we just configured inside the lib folder.
For the Switch component which has our routes inside of it, we wrapped it inside of the ApolloProvider component. Then we also provided a property the ApolloProvider component, which is the client we configured in our last step.
Once you refresh your code sandbox, everything still works fine.
Import useQuery Hook in PostsList
Now that we have our ApolloProvider configured, we can now use the useQuery hook in our different components. We are going to first use it in our HomePage.js. Let’s head into our PostList.js file and make the following changes first:
import React from "react";
import PostCard from "../components/PostCard";
import { data } from "../dummy-data/posts";
import { gql, useQuery } from "@apollo/client";
Here, the first thing we did was open up an import statement, then we imported gql and useQuery from @apolloclient.
Create GraphQL Query
Before we implement our gql and useQuery, let us make a connection between our React application and the WPGraphQL server using the Apollo client. We already have the apollo client part figured out.
Let us now take a look at the WPGraphQL server that we will be querying.
The first thing we want to do is copy the link from our uri, and paste it into the Online GraphiQL IDE. This is what it should look like:
Once you click on the little arrow icon on the right to enter, this is what it will look below:
What is happening is that it uses that GraphQL endpoint and makes a request to it, to interrogate the schema of that GraphQL database. This means that it is going to look at all the different items, types, nodes, etc, that it should have, and make that available in our query explorer.
What we are going to do now is use the Online GraphiQL IDE to visually construct our query, then run it against our test WPGraphQL database, then copy it and use it in our React application.
To get started, the first thing we want to do is head back into our code, go to dummy-data/posts.js and take a look at the shape of the data and what fields are on the data. Ideally, this is what the data in our dummy-data/posts.js looks like:
In our Online GraphiQL IDE what we want to do is replicate this data structure above.
To begin constructing our query as shown in the image above, to get all posts, we first have to go into the explorer section on the left and scroll until we find posts and open up the menu. When you open up the posts menu, you will see that GraphiQL will begin constructing a GraphQL query for us using visual outputs. Inside posts we opened up nodes, which will allow us to get the fields that we want on posts.
You can look at the dummy-data/posts.js data structure, and use that to construct your query, as demonstrated in the image above.
The next step is to click on the play button icon to run our data. When we run the data we’ll see that we get some of the data back on the right side of our Online GraphiQL IDE from our live WordPress install. This is a great way to preview your results to see if the query contains all the fields that you will need to construct the subsequent interfaces.
Format Query With GQL
The next thing we need to do is copy our query from the Online GraphiQL IDE, then go back into our PostsList.js file and edit the code to be like this:
import React from "react";
import PostCard from "../components/PostCard";
import { data } from "../dummy-data/posts";
import { gql, useQuery } from "@apollo/client";
const GET_ALL_POSTS = gql`
query MyQuery {
posts {
nodes {
databaseId
title
slug
author {
node {
name
}
}
featuredImage {
node {
altText
sourceUrl
}
}
}
}
}
`;
This is how we format our GraphQL queries for use with the useQuery hook or apollo client in general.
Fetch Data with useQuery Hook
In this next step, we are going to implement our query. So down in our PostsList.js we edited the code like this:
export default function PostsList() {
const { loading, error, data } = useQuery(GET_ALL_POSTS);
Here, we declared a new variable, then we reinitialized and did some destructuring to get our response from useQuery and passed in our GET_ALL_POSTS.
Making PostPage.js Dynamic
What we have created so far in our code sandbox are some common scenarios you will encounter when trying to create a headless WordPress site. Typically, most blogs or sites have somewhere where a list of posts are displayed, and also the ability for WordPress developers to use dynamic routes, as you can see in the image below:
To make our PostPage.js dynamic, let’s head into our PostPage.js component and make the following changes to our code:
import React from "react";
import { Link } from "react-router-dom";
import PostPageContent from "../components/PostPageContent";
import { data } from "../dummy-data/post";
export default function PostPage(props) {
const loading = false;
const error = null;
const postFound = true;
return (
<div className="page-container">
<Link to="/">← Home</Link>
{loading ? (
<p>Loading...</p>
) : error ? (
<p>Error: {error.message}</p>
) : !postFound ? (
<p>Post could not be found.</p>
) : (
<PostPageContent post={data.post} />
)}
</div>
);
}
Here, we imported React, then we have links from React-router which are going to be rendered. We are also importing some post-page content that we are passing data into. Right now, we are still getting data from our dummy-data/post.js folder, then we are going to replace that with data from our WordPress site.
Construct GET_POST_BY_SLUG Query
Back in our Online GraphiQL IDE explorer let’s make the following changes after unchecking the options we selected before. Now, instead of selecting posts as we did previously, we will open up the post, then select idType, then specify SLUG as our idType. In our case, we grabbed the slug for tyled-gallery and pasted it our id: “” as shown below:
Then we went ahead to select a couple of things in our Explorer, as shown in the image above. Once we run the query, we should get back data for just our tiled gallery post, as you can see on the right side of our Online GraphiQL IDE.
Call useQuery Hook From PostPage.js
Now that we have our query, we know that it is going to get the data we want, all we have to do now is copy our query, head back into the PostPage.js file and edit it to be like this:
import React from "react";
import { Link } from "react-router-dom";
import PostPageContent from "../components/PostPageContent";
import { data } from "../dummy-data/post";
import { gql, useQuery } from "@apollo/client";
const GET_POST_BY_SLUG = gql `
query MyQuery {
post(id: "tiled-gallery", idType: SLUG) {
title
date
content
author {
node {
name
}
}
categories {
nodes {
slug
name
}
}
}
}
`
export default function PostPage(props) {
const { loading, error, data } = useQuery(GET_POST_BY_SLUG)
const postFound = true;
The first thing we did was import { gql, useQuery } from “@apollo/client”;, then we declared a variable GET_POST_BY_SLUG and passed in our query. Then we created our const { loading, error, data } = useQuery(GET_POST_BY_SLUG). Everything should still be working just fine on the browser.
Pass Variable to useQuery Hook
In this next step, we are going to replace our hard-coded id with a dynamic variable that we can pass into our GraphQL query so that the query is different each time we hit a new route. To do this, we have to edit our PostPage.js to be like this:
import React from "react";
import { Link } from "react-router-dom";
import PostPageContent from "../components/PostPageContent";
import { data } from "../dummy-data/post";
import { gql, useQuery } from "@apollo/client";
const GET_POST_BY_SLUG = gql `
query MyQuery($slug: ID!) {
post(id: $slug, idType: SLUG) {
title
date
content
author {
node {
name
}
}
categories {
nodes {
slug
name
}
}
}
}
`
export default function PostPage(props) {
const { loading, error, data } = useQuery(GET_POST_BY_SLUG, {
variables: {
slug: props.match.params.slug
}
});
const postFound = Boolean(data?.post);
return (
<div className="page-container">
<Link to="/">← Home</Link>
{loading ? (
<p>Loading...</p>
) : error ? (
<p>Error: {error.message}</p>
) : !postFound ? (
<p>Post could not be found.</p>
) : (
<PostPageContent post={data.post} />
)}
</div>
);
}
The first thing we did here is we replaced our id: with $slug. What this tells the GraphQL queries is that this is our replacement tag. Then we open a bracket behind the query name, and passed in $slug: ID!. Down in our code, we have a variables property which also going to be an object, and then we passed in slug: props.match.params.slug. What we are doing here is passing props into our PostPage.js component, and it comes from our router. We also edited our const postFound = Boolean(data?.post).
Now, take a look at our finished project below:
If you look at this GIF, and compare it to the GIF shown earlier in this article, one of the things you will notice that is a little different, is that now that we are hooked up to a live WordPress site, as we click through our site, we can experience the loading at the top part of our site, which is the loading state from our useQuery.
Before we were loading all our data from our dummy-data files that were all local to the same file, so it wasn’t being queried or having to make a network call to get the items for our application.
Notice that when we enter a post, it loads, but when we go back to that same post it doesn’t load the second time. This is where our InMemoryCache we created with apollo comes into play.
Conclusion
In this tutorial, we created a simple headless WordPress application with React and WPGraphQL. Hopefully, you learned about how to do this style of development.
Link to complete code sandbox