
Headless WordPress Gutenberg & Next.js — Part 1/3: Creating a Block with React
The WordPress ecosystem is surely going towards JavaScript and non-PHP ways to develop plugins, themes and the core itself. Thanks to Gutenberg and WordPress JavaScript API, we can create some cool stuff. One of those would be a production-ready, WordPress static site builder that we’re going to code in this series.
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.
Intro
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
- getting use of React component we created above inside WordPress, as a Gutenberg Block using only JavaScript and React (we can also use many
@wordpress/*
packages such as@wordpress/blocks
,@wordpress/i18n
) - adding some basic styling and making the block interactive with extra JavaScript
- 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
.gitignore
later
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 -d
and --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)
- use some WP filters or hooks (but this can be also done with JavaScript
@wordpress/hooks
) - 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: index.php
.

Next, we’ll need to write some PHP code. We’ll need to:
- add a
<?php
declaration (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.

React Components
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?
Starting point
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 functional
or 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.
State
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.
Example component
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.


Note the usage of and React Hooks (e.g. useState()
) only in the parent component. I’ll explain later why it’s only in the top level component.
And that’s how the component looks like:


That will be enough of pure React for now. Let’s mix it with WordPress!

registerBlockType: "WordPressify" React component
This is the part where we’ll be combining JavaScript and WordPress.
As you probably know, JavaScript’s ecosystem allows us to use NPM for managing packages, just like Composer in PHP. @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.
Then, install @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 WordPressscripts
package. Then, to make use of it, add two scripts
to package.json
:
build
—wp-scripts build ./src/index.js
start
—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 package.json
before:

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 registerBlockType()
.
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 theedit()
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 edit()
.
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
<divs>
inside bothedit()
andsave()
— to use React Hooks inedit()
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!
Of course styles differ and JavaScript doesn’t work. Those problems will be covered in next episodes.
Summary
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:
BTW! I have one tip for you regarding the WordPress JavaScript API. Go to Post edit screen, open console and type 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?