In the first post in this two-post series, we learned that WordPress does not currently provide a complete server-side registry of Gutenberg blocks, and that as a result, there are two primary, viable options for rendering blocks in headless WordPress projects:
- Render Gutenberg Blocks as HTML
- Use WPGraphQL Gutenberg
The first post discussed #1, and this post will cover approach #2.
Before diving in, be sure to review the pros and cons of this approach listed in the excellent Gutenberg and Decoupled Applications post on the WPGraphQL blog to determine if it’s right for your project.
How WPGraphQL Gutenberg Works
As stated above, WordPress core doesn’t currently provide a complete server-registry of blocks. As a result, is not possible to query WordPress to get a list of all possible blocks and their data, and add that to the REST API / WPGraphQL schema. For headless sites, this means that you can’t fire off a REST API or WPGraphQL query to get all the blocks for a given post and have all the data you need.
- When the request is received, the blocks registry is saved to the database.
- Using the saved registry data, WPGraphQL Gutenberg adds the blocks to the WPGraphQL schema. This way, frontend applications are able to query for blocks and get all of their data.
To gain a more in-depth understanding of how it works, check out the project’s documentation.
Next we’ll learn how to work with WPGraphQL Gutenberg using this Next.js app as an example:
Here are the steps for getting set up:
WordPress Backend Setup
- Spin up local WordPress site.
- Install and activate both the WPGraphQL and WPGraphQL Gutenberg plugins.
- Create a couple blog posts using Gutenberg blocks to use for testing.
WPGraphQL Gutenberg Adminin the WordPress admin sidebar to be sent to the WPGraphQL Gutenberg options page, then click the
Updatebutton to update the blocks registry. This will loop through your posts, open the Gutenberg block editor inside of a hidden iframe for each, and save the blocks registry data to the database.
Next.js App Setup
- Clone down the Next.js app repo.
- Create a
.env.localfile inside of the app’s root folder. Open that file in a text editor and paste in
gutenbergdemo.localwith the domain for your local WordPress site. This is the endpoint that Apollo Client will use when it sends requests to your WordPress backend.
yarn) to install the app’s NPM dependencies.
npm run devto get the server running locally.
- You should now be able to visit http://localhost:3000/blog in a web browser and see the app’s Blog page.
pages/[...uri].js in a code editor. Our single blog post pages will be rendered using this file.
You can see that inside of the
getStaticProps() function, we run the
GET_POST query and pass to it the URI for the current page as a variable. Once the result comes back, we extract the post data and return it to send it through as a prop to the
SinglePost component in this same file.
SinglePost looks like this:
You can see that if we have blocks data to render, we map over the blocks and render a
Block component for each one.
/components/Block.js so we can take review the
Block component. It looks something like this:
This component is little more that a
switch() statement. It takes in the
block prop, determines which block should be rendered based on its name, then renders the corresponding block component.
In your project, you would need to account for all possible types of blocks at this point. Be sure to accommodate nested blocks (such as Column blocks) that need the
innerBlocks data passed to them as a prop, in addition to
attributes. Check out WebDevStudios’ Next.js WordPress Starter to see an example of how to do this.
If you have a 1:1 mapping of blocks to components without the need to wrap any of them in
divs/pass additional props/etc., you could even use this alternative approach which uses a JS object to map each block with its corresponding component.
At the top of the
Block.js file is this GraphQL fragment, which represents the data to be queried for all blocks:
Notice that the fragments for individual block types are also interpolated into this template literal. This fragment is used in the
[...url].js file we saw earlier with the
Now let’s take a look at an individual block component. Open up
At the top of this file is a GraphQL fragment in which we specify all the attributes we want included in our queries for Paragraph blocks.
Now take a look at the
ParagraphBlock component. You can see that we’re able to restructure the props passed in to pull out all of those same attributes.
Having the fragment and the destructuring assignment colocated in the same file like this makes it easy to keep the two in sync; you can see all the data being requested, and all the data being received all in one shot.
parseHtml() function the
content is passed through for the time being; we’ll discuss that in the next section.
To see examples of many individual block components beyond the ones in this app’s repo, check out these from WebDevStudios’ Next.js WordPress Starter.
Fix Internal Links
Custom Server-side Block Parser
Using this method, you’ll notice that internal links inside of Gutenberg blocks still point to the domain where your WordPress backend lives. You can fix that by using this Headless Block Parser plugin. Follow the steps in the readme to make use of it in your project.
With that plugin in place, an internal link pointing to
https://my-wp-backend.local/blog/hello-world in the post content will be rewritten to
http://localhost:3000/blog/hello-world, for example. So make sure that your frontend app’s routing is set up property to accommodate that.
An alternative approach you could take here would be to remove the domain, turning the links into relative URLs, such as
/blog/hello-world. If you go that route, just be careful to account for all possible URL permutations– those that contain anchor links or query string parameters, those that point to the homepage (
/), and so on.
Convert Anchor tags to Link Components
<a>). That means that when the user clicks one, a full page reload will be triggered rather than a route change using the SPA framework’s router. Let’s see how we can fix that and turn them into
If you open up the
package.json file, you’ll see that the html-react-parser library has been installed.
Head over to
/lib/parser.js now to see how it’s used.
parse() function that
html-react-parser provides parses the string of HTML into nodes. By passing in the
options object with a
replace() callback function inside, we tell the parser that when it encounters an anchor tag with a
data-internal-link data attribute of
true (an internal link), replace it with a Next.js
Now we can then make use of this new
parseHtml() function we created in our individual block components, like this:
As a result of this work, our site visitors will be able to click on an internal link inside of a block’s content and experience an instantaneous route change via Next.js’ router, with no more full page reload.
The example above that shows converting internal anchor tag links to
Link components is one use-case. You could use a parsing library to render components for other nodes inside of your blocks’ content as well though, if needed.
In the first “Render Blocks as HTML” post in this series, we saw how it’s possible to import several stylesheets from WordPress core that provide base styles for the blocks HTML.
Since this approach focuses on using WPGraphQL Gutenberg to query for JSON data and render all the block markup/JSX ourselves, however, you would also need to write custom styles that target each type of block.
Trade-off: Control vs. Ease of Implementation
As you can likely see, this approach gives developers a ton of control. You can query to get JSON data for your blocks all the way down to their individual attributes, then render and style them however you want. Implementing this approach requires a significant amount of work, however, so be prepared for that.
In the first “Render Blocks as HTML” post in this series, I described how it’s possible to use an HTML parsing library to replace a few HTML nodes with components, such as the
Link component that your JS framework provides. Working with WPGraphQL Gutenberg is quite the opposite experience; you must render components for every type of block yourself. If you need that level of control for your project, the additional effort could be worth it, though.
As I mentioned at the top of this post, be sure to also review the pros and cons of this approach listed in the excellent Gutenberg and Decoupled Applications post on the WPGraphQL blog to determine if it’s right for your project.
I hope this post gave you a good sense of what querying for and rendering Gutenberg blocks using WPGraphQL Gutenberg looks like in practice. I also hope it’s a helpful piece to reference reference if you choose to implement this approach in your own headless WordPress projects.
Do you have any questions about this method of rendering Gutenberg blocks content? Please reach out to let us know!