Headless WordPress Gutenberg & Next.js — Part 1/3: Creating a Block with React
Before we start
If you don’t know Gutenberg, React or WordPress on some solid level, this article might be too difficult for you. To familiarize myself with Gutenberg development, go ahead and read some articles from the official documentation. It’ll help you understand its purposes and used tech.
I always look for the best solutions and ways to code. Some can call me lazy, but I don’t like repeating myself in terms of anything, also the code. That’s why I spent some time on research and developed a way of building SSG-generated pages with WordPress as headless backend and Gutenberg as the page editor itself. All with one component structure source.
But how does it differ of all the other articles or tutorials on the internet?
My implementation is custom from the beggining to the end, which means we don’t use the predefined WP blocks (although we can!). We build our own blocks and can use it in three environments at once:
- admin-side WordPress (post edit screen) — as a fully functional Gutenberg block, with custom data fields, JS events, live editing etc
- client-side WordPress (front-end) — as a static HTML with optional styles and scripts
- Next.js (or Gatsby or Nuxt or other SSG/SSR framework) — as a fully functional React component and then a static HTML+CSS+JS
Here’s a quick interview of what we’re going to do:
Note that styles or the functionality on the video can differ from the one in the repository of the project as they could be updated.
As you can see in the video, the whole thing is about:
- creating a React component which will become our block
@wordpress/*packages such as
- creating a communication layer between WordPress and external apps (REST API or GraphQL)
- building a SSG-generated website with Next.js or Gatsby, using previously built blocks and a communication layer with (headless) WordPress
WordPress development environment
To kick off with our project, we’ll need to have a working WordPress instance. Personally, for such small projects, I use Docker and a basic
docker-compose.yml config file from the Docker docs. It’s very easy to use.
Please note that I’ve added a few lines inside the default file:
- lines 9–10: this will map our ports and allow us to connect to MySQL (with TablePlus or Sequel Pro) from outside the Docker container — it’ll be accessible on port 33061 defined in line 10
- lines 29–31: this will map our WordPress files outside the container and allow us to edit files locally, without getting “inside” the Docker container. it’s in a subfolder so it’s possible to add it to
You can now open terminal and type
docker-compose up -d --remove-orphans. This will run our Docker containers and “start up” our new WordPress Instance. The flags
--remove-orphans are described in Docker docs.
Now open your browser and type
http://localhost:8000 . Why :8000? Because we’ve defined this port earlier in docker-compose, in line 22.
You should now see the default WordPress’ config screen. Go ahead and configure the site.
The PHP part
As I said before, we’ll need to write PHP. But don’t worry — it’s only a few lines inside one file. And also it can be done once per whole project. What will we need to code in PHP?
- the plugin core (home of our blocks)
- enqueue block’s assets
First off, let’s create the plugin. In order to do this, create a folder inside
./wp/wp-content/plugins. I’ll name it
fancy-block. Inside this folder, I’ll create the main plugin file:
Next, we’ll need to write some PHP code. We’ll need to:
- add a
<?phpdeclaration (so the server understands) — line 1
- add a comment with plugin information in plugin’s header (for WordPress) — lines 2–8— as always in WP plugin or theme
- prevent from accessing the plugin outside of WordPress’ scope — line 10
- get our generated assets — line 14 (more info below)
- register the assets from pt 4 with
wp_register_script— lines 15–20
- register our Gutenberg block with
register_block_type— lines 21–24
- hook to
init— line 26
Good job! 👏
BTW. Don’t enable your plugin yet! It’ll probably crash your WP install.
First we’ll need to create the React component and any functionalities we’ll need in our future Gutenberg block, like events, API fetches, state or mocks, and all the other stuff.
React Component Generation
There are plenty of ways of starting a React project such as project boilerplates, frameworks etc. We could also create components straight inside our Next.js app. But that could be problematic to use with Gutenberg. I’ll use the well-known CRA in this project. We’ll use it to build our components, which we will then use as a base for our Gutenberg Block.
Why CRA? 😠
Must I say that it’s the least time-consuming way?
To start, go ahead to the plugin’s root directory and create a sub-folder for our components. Let’s just create it along with CRA:
npx create-react-app components
Next, we’ll need to just create our React component. I won’t cover this part in my article as it would take too long. You can find many resources for React online. Here are some tips though.
RFC or RCC
You can create
class components for Gutenberg. Both will work and…
- …YES! You will be able to use React Hooks such as
useEffect()inside functional components and…
- …YES! You will be able to use lifecycle methods such as
componentDidMount()inside class components
…and use all of them inside Gutenberg, on the WP front-end and in Next.js.
To manage state of your component, you can use any kind of React state management such as Redux, React Context or any other way, just as you’re creating a standalone React app. You can also mock your API, just like I did.
More on React state and lifecycle here.
I made a component which shows user’s data in a small box. In addition, there’s a modal/accordion which opens and closes on button click.
That will be enough of pure React for now. Let’s mix it with WordPress!
registerBlockType: "WordPressify" React component
@wordpress/* mentioned above are of course available there. And we’re going to use it.
Start off with creating a
package.json file inside root of your plugin (next to
index.php). To do it, open terminal, go to plugin root, type
npm init and go through all the steps to configure the package.
@wordpress/scripts by typing
npm install @wordpress/scripts --save-dev. As you can surely know, React needs compilation in order to run in the browser. That’s why we’ll need to import the official WordPress
scripts package. Then, to make use of it, add two
wp-scripts build ./src/index.js
wp-scripts start ./src/index.js
Now your package.json should look like this:
In order to run the scripts, let’s create the
./src/index.js file which we’ve linked inside
As you can see, I already installed, imported and used
registerBlockType function from
@wordpress/blocks and provided some data, like title, icon, category and empty attributes object to it. I also imported React as it’s mandatory to use JSX.
Here’s the above example in use:
The two nice functions — edit() and save()
According to official WordPress documentation, there are two handy functions we can use inside our
One of them is
edit() , which will be used when rendering the block inside
wp-admin post edit screen.
The other one is
save(). It will be used when saving the block as static HTML into the database (because all blocks are being saved to database as a static HTML!).
Remember that passing the same component to both is possible, but it doesn’t make much sense in my opinion. Usually the
edit() is much more complex than
save(), as we can use
InspectorControls and other good stuff from WordPress here. We also manage our sidebar “metaboxes” in
Note: more complex functionalities like CSS-in-JS or React hooks don’t work in
save() unfortunately. That’s why I’ve splitted the component into child and parent in the first place.
Let’s make use of our React component we’ve created before.
What I added here is:
- I’ve passed our React component to render inside Gutenberg now
- I’ve assigned some default attributes to block (just like I mocked the API inside component before)
- I’ve added some wrapping
save()— to use React Hooks in
This is how our block looks like inside Gutenberg:
And that’s how it looks on the front-end:
Try it out and play with it!
If you feel like missing or stuck somewhere, I also made a demo block, here: https://github.com/trykoszko/gutenberg-block-hydration-example. You can use it or contribute if you want.
I also published a video on Gutenberg Block Hydration about a year ago:
wp. You’ll see many of cool things!
That’s all for today. In the next parts of this article we’ll cover more advanced and in-depth topics, like Gutenberg Block Hydration and Headless WordPress Gutenberg + Next.js. They’ll come out soon.
Feel free to let me know any of your thoughts! Is this the future of WordPress? What’s your opinion?