person building a website using wordpress

WordPress has been around for a long time and is still one of the most popular content management systems out there. According to w3techs WordPress as a CMS powers over 40% of all websites on the web. WordPress is commonly used with page builders such as Elementor to build the UI of our websites.

What do we mean by a Headless CMS?

The term headless refers to us stripping away the frontend from our CMS for it to solely rely on acting as a data source. This allows us, irrespective of the technology used, to build a custom frontend of our website.

WordPress REST API is one of the many great features WordPress offers and allows us to access content on our CMS via a selection of REST API endpoints that respond with our content in the form of JSON. 

In the later tutorial I will be using React, the JavaScript framework, to create our frontend and for it to interact with WordPress via the REST API to create our Headless CMS + React application.

Note: you can use any technology to build your frontend as long as it can interpret JSON data.

Benefits of using a Headless CMS

  • Bespoke frontend. Can take advantage of what frontend technologies have to offer such as React, Vue and Angular.
  • Better security as we have separated our backend and frontend.
  • If you have a website already up and running and want to add in a blog feature, instead of building it all yourself you can use WordPress as a headless CMS to implement this quickly. 
  • Can serve multiple different outputs. I.e. multiple applications accessing the API and displaying its content.

Downsides of using a Headless CMS

  • You will require hosting for 2 projects instead of one, so a slight increase in hosting cost.
  • If you don’t know how to code, you will need to hire a developer who knows how.
  • The majority, if not all plugins are written in PHP and this will affect your decision in deciding whether a Headless CMS is a good fit for your project. Plugins that affect how your site would function or affect the frontend will not work as we have separated the frontend from WordPress.

React Headless CMS Tutorial

To follow along and complete this tutorial you’ll need to have a few things set up:

  • Knowing how to get a local installation of WordPress set up as I won’t be covering this in the tutorial.
  • Knowing the basics of WordPress and React would be beneficial.

WordPress setup

So you have your local WordPress up and running, first thing is to head to Settings -> permalinks to change our site’s URL structure. Good practice is to set it to Post name or Custom Structure.

Plugins

Next head to Plugins -> Add new. Search and activate the three plugins listed below:

CPT

Now I will create our Custom Post Type by going CPT UI -> Add New Post Type. I’ll name our post type ‘films’.

We will auto populate the rest of the fields by selecting ‘Populate additional labels based on chosen labels’. 

Scroll down until you see ‘Show in REST API’ and set this to true and enter in a slug ‘films’ into ‘REST API base slug’. This is to enable our post type to be returned within our API request later.

Scroll down again until ‘Supports’ and check the checkbox’s ‘Author’ and ‘Custom fields’ to enable these features to be supported for our custom post type.

Click ‘Save Post Type’. That’s our custom post type created! 

Custom Fields

Now time to create our Custom Fields for it to be attached to our ‘Films’ custom post type each time a new post is created. To do this go Custom Fields -> Add New.

Give your field group a title, I’ll call mine ‘Film Info’. 

Let’s add a field to this field group. Select ‘Add Field’ and fill in the ‘Field Label’, ‘Field Name’ and set ‘Field Type’ to text.

Scroll down to ‘Location’ and set the post type to equal our film custom post type.

Scroll back to the top and select ‘Publish’ to activate our custom field.

Time to create some posts for our custom post type.

Go to ‘My Films’ -> ‘Add New’ and fill in the following:

  • Title
  • ACF field ‘summary’ which will be at the bottom.
  • Featured Image which is in the right hand sidebar. 

Repeat this to create 2 more posts.

Now we have some posts created for our post type and we are now ready to test out our WP Rest API. To do this I recommend using chrome and install the Postman chrome extension. Postman allows us to test out our WordPress REST API endpoints. 

Once installed, open Postman and create a new request and enter the URL in the following format:

https://example.com/wp-json/wp/v2/films

Click ‘Send’ to see your results.

We now have our WP REST API up and running along with our custom post type and ACF fields ( “acf” -> “summary” ) being returned as JSON with our post information from the http API request.

Frontend Setup: React

I’m using Visual Studio Code as my text editor for creating our frontend, use whatever you feel most comfortable with. To create a React app you need to have NodeJS and NPM installed.

Within a terminal enter the commands:

  • npx create-react-app frontend (May take a minute or two). This will create and set up your react project within a folder named frontend.
  • cd frontend
  • npm i axios. This module will help us with our http api requests.
  • npm start. This will launch your react project.

If everything works as expected, we are all set to build our web application with React using WordPress as headless CMS.

Display Post Data

Create a folder named components in the src directory. Create a file named Films.js and enter the following.

import React, { Component } from 'react';
import axios from 'axios';
export class Films extends Component {
   state = {
       films: [],
       isLoaded: false
   }
 componentDidMount () {
   axios.get('http://localhost:3000/wp-json/wp/v2/films')
       .then(res => this.setState({
           films: res.data,
           isLoaded: true
       }))
       .catch(err => console.log(err))
   }
   render() {
      console.log(this.state);
       return (
           <div>
             
           </div>
       )
   }
}
export default Films;

App.js

import React from 'react';
import './App.css';
import Films from './components/Films';
function App() {
 return (
   <div className="App">
    <Films/>
   </div>
 );
}
export default App;

Inside of Films.js we created a react component named Films. Inside of this component we call the lifecycle method ‘componentDidMount’ to do a http request to our WP Rest API to get our Films data. We then output this data to the console. 

In App.js we import our Films component so it can be used within our render method.

Now in your terminal run the command ‘npm start’ to launch your react app. Open it in a browser and view the console to see our Film post data.

We don’t however want to keep our post data inside of the console. To display each film title we could map(loop) through each film within films like so: 

In Film.js.

 render() {
      const {films, isLoaded} = this.state;
       return (
           <div>
              {films.map(film => <h4>{film.title.rendered}</h4>)}
           </div>
       )
   }

output:

A more efficient way within React would be to separate the collection of films into a single film component.

Create a file within src/components names FilmItem.js and type:

import React, { Component } from 'react';
import axios from 'axios';
import PropTypes from 'prop-types';
export class FilmItems extends Component {
 
   render() {
       const { title } = this.props.film;
       return (
           <div>
              <h4>{title.rendered}</h4>
           </div>
       )
   }
}

Change Films.js render method to:

 render() {
      const {films, isLoaded} = this.state;
       return (
           <div>
              {films.map(film => <FilmItem key={film.id} film={film}/>)}
           </div>
       )
   }

Note: make sure you import FilmItem at the top of Films.js if it hasn’t done this for you already.

Here we are looping through each Film and passing it as a prop to the component FilmItem. This way we can encapsulate the logic of a single film post item to its own component. 

My final FilmItems.js outputting the ACF field and featured image for each post along with some CSS to make it all come together.

FilmItem.js

import React, { Component } from "react";
import axios from "axios";
import PropTypes from "prop-types";
 
export class FilmItems extends Component {
  state = {
    imgUrl: "",
    isLoaded: false,
  };
 
  static propTypes = {
    film: PropTypes.object.isRequired,
  };
 
  componentDidMount() {
    const { featured_media } = this.props.film;
    const getImageUrl = axios.get(
      `http://localhost:3000/wp-json/wp/v2/media/${featured_media}`
    );
 
    getImageUrl.then((res) => {
      console.log(res.data.media_details.sizes.full.source_url);
      this.setState({
        imgUrl: res.data.media_details.sizes.full.source_url,
        isLoaded: true,
      });
    });
  }
 
  render() {
    const { title, acf } = this.props.film;
    return (
      <div className="film">
        <div className="featured-image">
            <img src={this.state.imgUrl} alt={title.rendered} />
        </div>
        <h4 className="title">{title.rendered}</h4>
        <p className="summary">{acf.summary}</p>
      </div>
    );
  }
}
export default FilmItems;

App.css

@import url('https://fonts.googleapis.com/css2?family=Quicksand&display=swap');
 
* {
  padding: 0;
  margin: 0;
  box-sizing: border-box;
}
html {
  font-size: 14px;
  width: 100vw;
  height: 100%;
}
body {
  min-height: 100vh;
  width: 100%;
  display: flex;
  justify-content: center;
  align-items: center;
}
.films-container {
  display: flex;
  gap: 1rem;
  justify-content: center;
  align-items: center;
  margin: 1rem;
}
.film {
  max-width: 350px;
  flex: 1;
  align-self: stretch;
  font-family: 'Quicksand', sans-serif;
  box-shadow: 0 0 5px rgba(0, 0, 0, .3);
  border-radius: 5px;
}
.featured-image {
  flex: 1;
  height: 250px;
}
.featured-image img {
  border-top-right-radius: 5px;
  border-top-left-radius: 5px;
  width: 100%;
  height: 100%;
}
.summary, .title {
  padding: 0.25rem;
}
.title {
  font-size: 1.25rem;
}

Final output:

Summary

In this blog post I’ve given you a brief overview of what a Headless CMS is, some of its pros, some its cons and how to connect it to a React Application. This project is not just limited to React, it can be used with any other JavaScript framework. Also there are many more things you can do and expand on with WordPress Rest API, so feel free to expand on what we’ve looked at today.