Introducing the React-Gutenberg Bridge: Headless Block Support for an Even Better Editing Experience
You’re excited about the opportunities headless WordPress offers, but your client’s marketing team is tied to the WYSIWYG Gutenberg editor.
See how Faust’s new Gutenberg block support for headless projects brings the two together to modernize your development while empowering your marketers.
- Teresa Gobble, Software Engineer at WP Engine
- Blake Wilson, Senior Software Engineer at WP Engine
TERESA GOBBLE: Hi, folks. My name is Teresa Gobble. I’m a software engineer with WP Engine working on the Faust team.
And I’m here with Blake Wilson, a senior software engineer, to introduce you to the React-Gutenberg Bridge– headless block support for an even better editing experience. Welcome. Let’s get started.
So today, we’ve got a lot to cover. First of all, I’ll go through a couple of things related to the problem and the solution that we have for you, as well as the value of React-Gutenberg Bridge. Then we’ll go to Blake, who will provide us with a demo of the React-Gutenberg Bridge in action. Afterwards, we’ll talk about a couple of technical details. And we’ll also visit a future roadmap of what we have in store for this.
So here’s the problem. There is no streamlined way to translate Gutenberg blocks from WordPress to a headless front end. The solutions out there that do exist aren’t scalable or intuitive yet to provide a developer experience that headless developers can expect.
Decoupling breaks the ability to use Gutenberg block content in the editor naturally. And agencies are left to wonder how they go about making it their own way or from scratch with little guidance. And a lot of unanswered questions remain for folks.
What about styling? What about reusability, dynamic blocks, InnerBlocks? Well, here’s where the React-Gutenberg Bridge comes in. It’s a solution in two parts– first, a way to programmatically expose Gutenberg blocks so that they can be parsed and read on the headless front end. This piece is called WPGraphQL Content Blocks.
Secondly, we have a connector to facilitate the setup and rendering of those blocks in the headless front end. And this is a package called Faust WP Blocks. Here you’ll see a walkthrough of how it works with both of these solution pieces.
Your website’s React-based back end has its Gutenberg Blocks, which are exposed by the WPGraphQL Content Blocks plugin. It exposes the block.json content to WPGraphQL. It provides it to the plugin, called WPGraphQL.
And then it comes over to the connector package, which enables customization, discovery, and rendering of blocks. And this will actually be discussed a lot more as we go about the technical discussion and the demo today. So what kind of value does this bring to your team?
Well, it’s an end-to-end opinionated solution, which reduces complexity and ambiguity. It saves development time by following specific conventions. It allows blocks and block patterns to be used in combination. And it can be reused over and over again. Now that you’ve got an idea of how the React-Gutenberg Bridge works, let’s go to Blake to see a demo of it in action.
BLAKE WILSON: Thanks, Teresa. Hi, everyone. I’m Blake Wilson. I’m a senior software engineer here at WP Engine.
And I’m on the Faust JS team building Faust. I’ve got a really great demo for you today showing off the two components that we have built to help orchestrate this React-Gutenberg Bridge. So let’s get right into it.
To start things off, I’ll show you what I’ve got here for my setup. And then we can go into the actual code and see what we’ve got there. So for starters, I’ve got a WordPress site here running on Local.
I’ve got a few plugins installed. So I’ve got the Faust plugin. This helps facilitate previews and all that kind of good stuff on your Faust JS site. I’ve got WPGraphQL, which is necessary for transforming your WordPress site into a GraphQL endpoint.
And then I’ve got WPGraphQL Content Blocks. So this is one of the plugins that we’ve built to help facilitate this React-Gutenberg Bridge. This solution is in two main parts.
So we’ve got one of the pieces to actually expose Gutenberg Block data programmatically through WPGraphQL, and then another portion to consume that on your Faust JS front end. Let’s start off by taking a look at WPGraphQL Content Blocks and how that works.
So we’ll pop into our graphical IDE. And I’ve got this query set up here to grab a page’s data. So in this case, we’re just getting the page’s title.
So what GraphQL Content Blocks does is expose a content blocks type in your GraphQL schema. So if we type in content blocks, you can see here, we get information for this given page and all of the blocks on this page. Let’s go over and edit this page and add some content.
So we’ll pop in to sample page. And you can see here we’ve got a blank slate. So let’s go ahead and create some blocks. Let’s create some columns here.
And we’ll do a 50/50 column. Let’s add a paragraph on this half, and then in an image on this half. So I’ve got an image here in my media library. Let’s go ahead and drop this in.
And you can see here, we’ve got two columns. Again, a paragraph on the left, and an image on the right. So let’s update this. And let’s go back to WPGraphQL Content Blocks and see what we get as a result.
So you can see here, now we have two content blocks. First one here is a core column, core column. And then we get rendered HTML inside that.
So the great thing about WPGraphQL Content Blocks is we’re handling InnerBlocks as well. So you can see here if we add a param to content blocks called flat true, you can see now we get actually all of the blocks that were even in those columns. So we’re handling that case for you as well.
We get a core column, core column, core paragraph, core image. So all of that is done programmatically for you. And now, you can use this block data on your front end. So let’s dig a little bit deeper here.
Let’s say we want some of the attributes on that. We can use that using a union in GraphQL. So we’ll do on core image, get the attributes. And let’s say we want the caption, for instance.
So you can see here there’s no caption. Let’s go back to our sample page. We’ll go ahead and add a caption here. My caption. Update that.
And if we refresh this query, we can see now, we’re getting my caption as a proper attribute in WPGraphQL Content Blocks. So this is part 1 of the solution. Now, we can actually get all of our Gutenberg Block data and use this to consume it on our front end.
So let’s pop over to VS Code, and we’ll see how we tackle that piece. So this is a Faust JS example project that I put together. It’s very simple. It’s based on the Faust Scaffold Blueprint, but with some additional configuration for handling these blocks.
So if we take a look at the package JSON, you can see here we’ve got a few dependencies here, some of the usual Faust packages, like core and CLI. We also have Faust VP Blocks. So this is one of those packages that provides all of our helper functions.
We’ve also got some at WordPress dependencies for handling styles and so on. You’ll also notice here that we’ve got this WP Blocks directory. So this is where all of our blocks for our front end live, and acts as a registry for all the blocks that we use on our front end.
You can see we have an index.js file. And this is essentially an object that determines all of the blocks that we’re using on our front end. So you can see here, we’ve got core paragraph, core column, core columns, and core image.
In terms of setting this up, there’s two main pieces that we’re going to be talking about. So one is the WordPress Blocks provider, and the WordPress Blocks viewer. So let’s take a look at what that looks like in action. Let’s first take a look at the WordPress Blocks provider.
And this is going to be available in pages_app. So you can see here we have this component, this provider, WordPress Blocks provider. And it accepts a config prop that accepts blocks. So you can see here we’re importing blocks from WP Blocks, this directory’s index, and we’re passing it into the config object.
So essentially, what this is saying is the WordPress Blocks provider wraps your entire app and gives context for all of these blocks to your entire app. Now, let’s go into WP Templates into our singular template. And you can see here we’re calling WordPress Blocks viewer with a prop of content blocks. So this is the block data that we get back from WPGraphQL.
All right, that’s enough about the setup. Let’s spin this up and see what it looks like in action. So I’m going to run NPM run dev, which will set up a dev environment on localhost 3000. And the page that we were working on before was slash sample page, so I’ll visit localhost 3000 slash sample page to see those Gutenberg Blocks that we set up before.
So you can see here, we’ve got the blocks that are the same in our Gutenberg editor. So let’s go back to our Gutenberg editor for sample page. And you can see we’ve got our two columns here, this is my paragraph, and then our image, which corresponds to what we’ve got on our front end here.
So you might be saying, that looks great and all, but can we modify styles? Can we change font size? You sure can.
So let’s go back to our Gutenberg editor and make some modifications to these blocks. So let’s add a background color here to our paragraph. Let’s also change the size to a large. For this image here, let’s make it rounded.
Let’s take out the caption. And we’ll update that. And you can see here those styles now apply. And you can see them on your front end.
So we’re really giving back the publisher experience that you don’t expect in WordPress to your headless WordPress site. Another great thing about this is now that you’re getting programmatical data for these blocks, you can make your React component with framework-specific features, like next image. Now, instead of just rendering the HTML that you get back from WPGraphQL, now we can use that programmatical data to create a component that renders all of our images in Gutenberg with next image, giving us lazy loading, better performance, and better optimized images overall, creating a better user experience for our users.
So this is great. We’re seeing exactly what we expect in our Gutenberg editor, but let’s say we add a component that maybe is not supported yet, or that we haven’t configured in our Faust site. So let’s go ahead and create a new component down here. We’ll use table.
And we’ll do two rows– row 1, row 2. Go and update that. And if we look back in our code here, we can see we’ve got four blocks defined– core paragraph, core column, core columns, and core image. We don’t have core table here.
So what’s going to happen when we view this page? Let’s take a look. So we’ll go back to sample page on our Faust front end. And you can see we still get a table here with row 1 and row 2.
That’s because if the block is not defined yet in your Faust JS project, we’ll do a sensible smart fallback to the rendered HTML. That way, you’re not seeing undefined, null, or just no content at all. At the very least, you’re getting back the original rendered HTML.
With all this in mind, let’s take a look at what it actually takes to create a block– what it actually looks like. So we’ll go back to VS Code here. And let’s pick on the core image for instance.
So you can see here, this is just a traditional React component. We’re calling it core image. And it accepts props, just like any other React component.
There’s essentially two pieces to a block here. So we’ve got the actual React component, which is the presentational layer. And then we get the block.fragments, which is the data that’s needed for this block to perform.
So you can see here we’re creating a fragment, core image fragment on core image. And we’re getting these attributes– the attributes that we need to render this block. So you can see we’re getting the alt text, the source, the caption, class name, width, height, and so on.
And then what we can do is apply those attributes into our actual React logic. So all of the fields that are requested here are then available in props. So you can see props.attributes come out, which is the attributes that we requested here, attributes.alt, attributes.source, and so on. So this is a great way to colocate all of the data requirements for your block within the same file.
This is making sure that you’re only requesting data that you need, and making sure your requests are nice and performant. We’ve got a few helper functions as well in this example project. You can see there’s a couple here– get styles and get image sized props.
So these are essentially just taking these styles from WordPress, and combining them into an actual styles object that React can use. Currently, styles are supported for inline styles. You can also get global style sheets as well, but we’re currently working on providing support for theme.json.
So Teresa will talk a bit about this in our future roadmap. But ideally, there will come a point where we can get all of our styles and padding, margins, and so on from theme.json and apply that here in the headless front end. With all that in mind, let’s pop into a quick technical discussion with Teresa and I to talk about where we’re at today with this feature, and where we’re planning on going in the future.
TERESA GOBBLE: Thank you for that demo, Blake. That was great. Let’s go ahead and get into some technical details now, and chat about how this works. So the first one I’ve got for you is, what are the requirements to use WPGraphQL Content Blocks?
BLAKE WILSON: Yeah, yeah. Great question, Teresa. So the only requirement to use the plugin is to have WPGraphQL installed as well. Obviously, if you want your site to interface with Faust JS, you can install the Faust JS blocks package, which will help facilitate rendering and all that good stuff on the headless front end. But to actually expose block data, all you need is WPGraphQL and the WP GraphQL Content Blocks plugin.
TERESA GOBBLE: Awesome. How is the block data gathered as well?
BLAKE WILSON: Yeah, so all the block data is gathered by any block in WordPress that uses the register block type function. So pretty much any block that you’re using that interfaces with that function will show up in content blocks. And the great thing about that is it relays with the block.json file and automatically self-describes and self-documents all of those fields. So you’ve got documentation all in one.
TERESA GOBBLE: Oh, awesome. What a time saver. Another thing that I’d love for you to talk a little bit more about is just what happens with an unsupported block? What happens when an unsupported block is queried?
BLAKE WILSON: Yeah, that’s another great question. There’s two real scenarios that can happen here. So there might be an instance where let’s say you have a block in your post data that has since been removed from WordPress.
Maybe it was a third party block that’s been removed. So that is one case of an unsupported block that is unsupported both in the Faust front end and the WordPress registry. In that case, we actually return a block to content blocks called undefined block or unknown block so that you can type that appropriately in your headless front end. And then the second part is if a block in the WordPress registry is supported, but it’s not yet supported in your Faust JS front end, in that case, what we do is we fall back to the rendered HTML. That way, at the very least, you have rendered HTML that’s showing up, not null, or undefined, or any values like that.
TERESA GOBBLE: Oh, awesome. And this actually leads me to my next question. As far as third party plugins in a headless decoupled website, can you use a third party plugin by using the WPGraphQL Content Blocks plugin? How does all of that work together?
BLAKE WILSON: Yep, yeah. So for any third party plugin, going back to that first or second question, as long as they interface with the registered block type function in WordPress, that block will automatically be exposed to WPGraphQL Content Blocks. So as long as that data is being rendered, you can then create the block in your Faust JS front end. And the great thing about that is let’s say you have a third party block for a carousel. Once you’ve created that once in your Faust JS front end, you can then reuse that in other projects going forward.
TERESA GOBBLE: Oh, great. That’s where the reusability piece comes in. And with this plugin, you can actually bridge some of that gap with third party plugins not working out of the box with the decoupled websites.
Also, if you look in the chat now, we actually have a tutorial built out to help folks create a block from a third party plugin. So you look in the chat now, you’ll be able to see that and give it a click. Give it a bookmark.
So how do you handle blocks within blocks? That can be really tricky. Can you talk us through a little bit of what that would look like?
BLAKE WILSON: Sure, yeah. So we have this flag or this parameter when you’re querying content blocks called flat. And that either accepts a true or false value. And so when this is provided as true, you’ll actually get a flat array or flat list of all the blocks on that page, whether it’s a column, or an image, or a paragraph.
You’ll have a full list of all the blocks queried on that page with two additional properties. One is the node ID. So that’s the actual ID of that particular block. And then you’ll have parent ID as well, which is the parent of that block. So what you can then do is reconstruct that into an actual hierarchical list on your front end, pretty much solving the inner block conundrum as we’ve seen in Gutenberg before.
TERESA GOBBLE: Awesome. So actually, there’s an option, when fetching content blocks, that you can specify to return a flat list of blocks within their appropriate parent child IDs?
BLAKE WILSON: Yep, yeah, exactly.
TERESA GOBBLE: Great. We also actually have another piece of tutorial down here in the chat, again, for the WPGraphQL Content Blocks to take a look at that particular function, too. So I wanted to ask you another question about the styling piece– so styling with global style sheets, inline, what is the approach there? How is styling handled?
BLAKE WILSON: Yeah, yeah. So styling is probably one of the biggest pushes that we’re trying to research right now. In the example that I just showed, that’s using inline styles.
Global styles, global styles sheets are also supported. And I think you’ll be touching on this next in the roadmap. But ideally, we want to also support theme.json support, where we can get margins, paddings, colors, and all that good information from the theme.json, and then apply that. So we’ll be working on that in our next phase of development.
TERESA GOBBLE: Awesome. Thank you for walking us through that. I know a lot of people are really excited about that. So how do we restrict the publisher from using blocks that are not supported?
BLAKE WILSON: Yeah, yeah. So there’s a plugin out there. It depends. If you’re using third party blocks, some of these already have this feature built in.
But if not, there’s a plugin out there called block visibility, which you can actually toggle specific blocks from the publisher perspective. So let’s say you have a carousel block that hasn’t been implemented on your Faust site yet. You can install block visibility, and uncheck that so the publisher doesn’t use it while it’s still unsupported or in development.
TERESA GOBBLE: Oh, awesome. So that plugin block visibility can actually toggle, hide, show specific blocks?
BLAKE WILSON: Yep, yeah, exactly. So that way, you’ve got a limited number of blocks that you’ve supported, both in your WordPress side and your headless site so that the publishers know, OK, we can use this with certainty that it’s going to be supported on the front end.
TERESA GOBBLE: Oh, that sounds like a cleaner delivery for sure. OK, cool. Last question for you. Do these front end blocks correspond to the publisher’s editor?
BLAKE WILSON: Yeah, great callout. So not yet. That is something that we’re going to be working on in the future, but for right now, these blocks are supported for the headless front end.
If you have a custom block that you’ve created in WordPress, if you’re using the NPX create block command, you’ll still need to support that view on the WordPress side. But it is something we’re working on. We’ve got it in our roadmap.
TERESA GOBBLE: Oh, awesome. OK. Thank you for talking through those points with us, Blake. That’s been really helpful, and the demo as well.
Let’s go ahead and switch gears and actually talk about that project roadmap a little bit more. We actually have five phases, two of which are already completed– phase 1 and phase 2. Phase 1, we saw an implementation of a method to deconstruct and then reconstruct a block efficiently.
After that, we moved to phase 2, which was a focus on tighter Faust integration with Gutenberg Blocks in order to ensure that folks have all access to the different utilities and helper functions that are in there. This next phase we’re actually on right now, phase 3, we’re focusing on providing theme.json support and reusable block libraries, as Blake mentioned during our technical discussion.
After we’ve got that done, phase 4 and 5 will happen. Phase 4 is a focus on enhancing the existing development and editing experience, as well as phase 5, which is a focus on supporting the wider ecosystem beyond core WordPress. We’re really excited for these phases that are coming up, and we hope that you touch base with us and take a look at our blog post as well to keep up to date with where the roadmap is.
You can see a link in the chat below to our blog posts if you take a look. Go ahead and bookmark those. Well, thank you everyone, for joining us for our discussion for the React-Gutenberg Bridge. I want to bring Blake back onto the screen here so he can say his thanks as well and give us a little bit more info as to where you can go if you have any lingering questions after this.
BLAKE WILSON: Yeah, thanks, Teresa, and thanks for everyone that joined this session today and watched. We’re really excited to get some community feedback around this feature for you all to start trying it out.
So if you do like it, we have got the example project in the link in the chat. We also have a link in the chat for our headless Discord, so just a place where you and other like-minded headless devs can join and chat about upcoming features and releases in the headless space. So thanks to you all again. We really appreciate it.