Build Custom Blocks Using InnerBlocks

A great way to start building blocks for WordPress is using InnerBlocks. Group other blocks together and easily add styles and settings.

👉 Full Code Repo
👉 WordPress Create-Block Package

What’s the fastest way to make a custom block in WordPress? A block made of other blocks! In this new video I teach you how to make a simple custom block using only InnerBlocks. A quick 🧵 on why:

Even the fanciest designs are often just groups of basic HTML elements- images, headings, paragraphs- things that WordPress already offers as full-featured blocks. You can put them together in the editor and save them as block patterns- or you can go the custom block route. It depends on what you need.

For me, the main reason not to use a pattern was the amount of custom CSS I wanted to add. By starting with wordpress/create-block I was able to write Scss and enqueue stylesheets just for my block on the frontend and in the editor. With block patterns, custom CSS is not so straightforward and you end up making “Group” soup.

Once you decide to build a custom block, you’re in charge of the editing experience. You can import the  React components from WordPress (RichText, TextControl, etc), but that may be overkill. Using nothing but InnerBlocks takes only a few lines of code, and the markup it generates is very clean.

You get one wrapper div around the blocks, so you can use things like CSS Grid to get complicated designs and layouts. Plus you can use things that already exist (like “supports” and custom block styles) – on the parent block OR on the child blocks – to make the design even more flexible.

Building a block with nothing but InnerBlocks is quick and easy- which means it’s not going to be right for every situation and it will have some limitations. That said, I’ve done things like build an entire carousel out of nothing but the Media & Text block, so cool things are possible.

One last note: Props to Aurooba for originally showing me this type of custom block. If you’re interested in building a high-quality editing experience for your block, check out her course: Thoughtful Block UI for WordPress.

Full Transcript

 Hello and welcome. Today we are going to create a block using the InnerBlocks method. So what does that mean? It means we’re going to make a block for WordPress, but what we’re going to do is not write any sort of custom components inside or and really write much code at all. We’re actually just going to use existing core blocks and stack ’em together to make our own block.

It’s a really great way to throw something together kind of quickly where you don’t need custom components. Maybe you just need a heading, an image, a paragraph- you know, things that already exist in WordPress as blocks. You just want to package them in a certain way. And maybe making a block pattern isn’t the best approach because you wanna add some extra settings to it, or some extra CSS that kind of comes bundled with it.

So this sort of “block made out of other inner blocks” method is pretty quick to throw together and pretty flexible. We’re gonna make a little design pattern that I’ve done in some sites recently, and so I think it’s gonna be a good example of how we make a block with an inner block. Let’s start off here with this repo.

Our environment and create-block

What we’re looking at is a scaffolded block plugin from the “create-block” tool. I ran npx wordpress/create-block. It made a little plugin for me, and that has everything I need. It runs npm install, so you can see all my node modules sitting right here. It’s got a source folder that it scaffolds out with all the stuff you need for a block.

So I ran one command and all of this code was automatically generated. I’m going to use it to create my block. So let’s start over in our terminal, just running npm run start. It’s in our inner-block-example folder. It’s just going to run and watch all the code and compile everything from our src folder to our build folder.

The next thing that you’ll need to do is enable the plugin in your WordPress installation. So make sure the plugin is turned on. Sometimes I forget that one and I can’t find my block, and I don’t know why. 

So here we’re just looking at the backend of a WordPress website. Essentially this is where a block’s gonna sit in the column next to some text. As we put it together, you’ll see it’s a pattern that’s pretty popular. So if I were to go and add the block here, “Example inner block” It’s just that stock code that comes from the create block package. “Example inner block. Hello from the editor,” in a nice blue box. So we’re gonna delete this and we’re gonna actually start building out our block in the code.


So the first place we’re gonna start is with our block.json file. There’s actually not much we really need to do here. All I want to do is add another “support” for now. We might come back to this later. I just want to turn off alignment on my block. I don’t really need full width alignment or anything like that.

Now you’ll notice that npm run start fails if you, god forbid, save your block.json file with a mistake in it. It will just fail. So I will run npm run start again. I had a little trailing comma right here, so I apologize. Oh, it’s still mad at me- I didn’t save it. Let me save my change. Let’s run that again. Okay.

	"$schema": "https://schemas.wp.org/trunk/block.json",
	"apiVersion": 3,
	"name": "create-block/example-inner-block",
	"version": "0.1.0",
	"title": "Example Inner Block",
	"category": "widgets",
	"icon": "smiley",
	"description": "Example block scaffolded with Create Block tool.",
	"example": {},
	"supports": {
		"html": false,
		"align": false
	"textdomain": "example-inner-block",
	"editorScript": "file:./index.js",
	"editorStyle": "file:./index.css",
	"style": "file:./style-index.css",
	"viewScript": "file:./view.js"
}Code language: JSON / JSON with Comments (json)

So all I’m doing is turning off alignment in block.json. You know, maybe I would probably want to change the name and all that sort of stuff because this does affect some things, but at this point I think I’m okay to leave it.

Edit.js and Inner Blocks

So here’s our basic edit.js file. This is where we saw that sort of paragraph with the blue background and the example text right here. I’m actually gonna get rid of all this. I don’t need any of this stuff. 

I do want to take my block props though. So what I’m gonna do, I’m gonna copy these here and I’m just gonna save them as a variable: blockProps. And then I’m going to use a new function here that I’m gonna import called useInnerBlocksProps. So you’ll see the “s” here. It’s a little bit different than what you see there, so just keep an eye on that. So we’re gonna use innerBlocksProps. So you can see GitHub copilot kind of knows where I’m going with this.

The first thing I’m gonna pass to useInnerBlocksProps is our blockProps. So basically that’s saying take the props, you know, all the stuff that I have for my parent block and using that to generate the props that I need to show my inner blocks. So it’s gonna kind of combine that stuff together.

And what’s great is, you know, with this approach, because we’re doing a lot of this stuff here, What actually gets rendered is very clean HTML versus like a pattern where maybe you have to put it in a group and maybe you have to put that inside of another group, you know, and that sort of thing. We’re, we’re gonna just trim this down to basically one sort of wrapping div, which is our block. And then the blocks right inside of it that we need. 

So the first thing that it’s gonna want is our little options here. So the first thing it’s gonna ask for is, what blocks do we want allowed in here? So the example they give is the core paragraph.

We’re actually going to do the core/image block. That’s the only block that we want allowed. We don’t want them to be able to insert their own blocks. We don’t want them to be able to add other blocks. We just want images. This is gonna be a wrapper block with only images inside of it. Next is the template parameter, which basically says: when they insert our example inner block, what should it look like first? And so they give you an example of an array with the first one being the core image. What I actually want is two core images, just like that. 

And then the last thing that you’ll often want is to decide if you wanna lock it or not. So, What that means is this is gonna be a block and it’s gonna have two images inside, but if I don’t lock it, they could keep adding images. They could just, you know, add more and more images inside of our parent block. And, and there’s lots of situations where you would want to do that. There’s lots of times where that’s the kind of thing you’re building where you want them to just be able to keep adding inside. In this particular case, we don’t. We want them to only be able to put two blocks Inside of this. So it’s gonna load, it’s gonna have two images in it, and then that’s it. They, they kind of can’t mess with it after that. 

So let’s change this paragraph to a div and then let’s actually spread out our innerBlockProps. There we go. So what that’s basically saying is it’s gonna return this div and it’s gonna spread all of those props out, you know, as class name and size and you know, all those attributes that kinda get stuck at the top of the div.

export default function Edit() {
	const blockProps = useBlockProps();
	const innerBlocksProps = useInnerBlocksProps(blockProps, {
		allowedBlocks: ["core/image"],
		template: [["core/image"], ["core/image"]],
		templateLock: "all",
	return <div {...innerBlocksProps}></div>;
}Code language: JavaScript (javascript)

So let’s start there. We’re gonna save that and make sure we have no errors, make sure we’ve imported everything correctly. And that’s really all you need for the edit.js file. I mean, you just tell it what sort of blocks you want, and you tell it what you want the template to look like. There’s some other stuff you can kind of set here, but in this example, that’s really it.

Preview of the editor

Let’s head over to the editor and see if it works. I haven’t made the save.js file, so it definitely won’t save, but maybe we could actually see what it looks like a little bit in the editor. Okay, so we’ve reloaded our editor. Let’s add it. And boom. So here we go. You can see it’s still got that blue background ’cause that CSS is still there, but there’s our smiley face, that’s that kind of inner block and inside two separate image blocks.

So if I open the list view, you’ll see here’s our block, here’s our two image blocks, and they’re just locked. So you can’t really move them. You can’t add new ones. You can’t get rid of ’em. They’re just there, but they’re full image blocks. So they have a lot of the settings that a normal image block would have, and it’s all kind of sitting right there.

So nothing’s gonna work. Now, it’s not gonna save or anything ’cause we need to set up the save.js file. But again, that’s pretty easy to do. So let’s head over and take a look at making sure that this saves properly. 


Okay, so we’re over in our save.js. There’s a few kind of small things we need to do.

Number one is we need to make sure our paragraph is a div. We can get rid of this. And what we actually want in here is our InnerBlocks. So let’s start typing in InnerBlocks and you see it already recommends it here, <InnerBlocks.Content>. And that’s gonna basically take our content and spit it out from the inner blocks and save it.

And you know, because it’s a block when it comes back and gets edited again, it’s kind of got all the information it needs. So that should be good, except we actually need to import InnerBlocks. But yep, it’s just a div with our blocks inside of it. I mean, that’s really it.

export default function save() {
	return (
		<div {...useBlockProps.save()}>
			<InnerBlocks.Content />
}Code language: JavaScript (javascript)

And so now that we have edit.js and we should at least be able to save our block, and then we can take a look at kind of styling it and like what we can do with something like this.

Testing our save.js

Okay, so let’s come over here. Let’s add our “example inner blocks”. Let’s click on the first image and add something. We got the nice moon. Let’s go to our second image. Let’s add something, some more moon. We’re gonna hit update here and let’s go check the front end. 

Okay, so here’s the front end. Let’s scroll down to the content area, and there we go. We got our two images put together. It basically treats it like one block. 

Reviewing the frontend HTML

We can even do a quick look at the code. So, this is our block, the .wp-block-create-block-example-inner-block. So, .wp-block kinda goes in front of it. create-block was in the block.json file with the name of it.

So there’s that wrapper div that we’ve created, and then you’ll look inside. These are just the two blocks. I mean, <figure> elements is how they’re put together in WordPress. But that’s pretty much it. This is the only HTML we’ve really added on top of here is this right here. And because this is a custom class that’s, you know, scoped to the name of our block.

We can use it to write some CSS and give this little inner block a design. So let’s take a look at what we can do. 

Styling our block

Okay, so we’re looking at the CSS. You can see that’s that background color and everything that they add to it. We’re gonna get rid of that. What we’re gonna do is we’re gonna make this a relative position element because we’re going do a little absolute positioning here. So, we’re gonna target the figure element, which is basically the image block. I could probably also say .wp-block-image. I could target that as well. It kind of doesn’t matter. Let’s see. So I have a few things I want to do here. 

Let’s start with that caption. I’m just gonna display:none that for now because I just don’t really want see the captions, so we’ll just hide those. Maybe I would consider doing it from an accessibility standpoint. We’re basically removing them so they won’t be read. If I do want the captions to be read, which I probably don’t, ’cause the images should have alt text, but then I would do maybe more of like a visually:hidden type of approach.

So let’s look at nth-of-type(1), basically the first image. And what I want to do with the first image is scale it down a little bit. So we’re gonna do transform:scale(0.9). I’m just gonna make the image a little bit smaller. 

The second image, what I want to do with the second image is I want to get it to absolute position and kind of like be a little offset from it. So we’re gonna do a position:absolute, we’re gonna need that top:0 and left:0, and then we’re gonna transform and we will also scale it down. I think the reason there is: when you’re kind of overlaying images like that, it kinda helps to shrink ’em a little bit so you don’t end up covering content.

So we’re gonna scale that one down and then we’re gonna translate it. We’re gonna move it over, not nearly that much. We’ll just move it over a tiny bit for now and we’ll save that. That should be everything. Let’s take a look and see if I missed anything. 

Previewing our frontend styles

Okay, so here we are on the frontend. I’ve refreshed it and now you can start to see the effects of what we did.

So here’s our block. Here’s our first one. You can see the scale down over here. It’s been scaled. And then the second one, it’s absolute positioned and it’s kind of scaled and it’s kind of stuck off to the side a little bit. I feel like that 10% is not quite enough. Let’s just boost that up a little bit.

Okay? So negative 20% each. Save and refresh. There we go. There we go. That’s that nice effect, right? So, You get to pick two images on the back end of WordPress, and then you come to the front end and you get this kind of cool effect. And so, you know, whatever the background image is, it’s kind of not meant to be seen, but you know, it adds a little bit of a cool texture to it.

You could even do two of the same image or something. I don’t know. There’s probably some cool ways people have used this pattern. But yeah, that kind of gets it going. 

.wp-block-create-block-example-inner-block {
	position: relative;
		max-width: 100%;
			display: none;
		&:nth-of-type(1) {
			transform: scale(0.9);
		&:nth-of-type(2) {
			position: absolute;
			transform: scale(0.9) translate(-20%,-20%);
}Code language: SCSS (scss)

Reviewing the editor experience

Let’s take a look now that we’ve added our CSS to the backend of WordPress, because you know, when you do this kind of stuff, you always have to go back and say, okay, what does it look like when I try to edit this block?

Now that I’ve done some kind of fancy styling, okay, so I’ve refreshed WordPress. You can see we still have the dotted red line that comes from the create-block package, which we need to fix. 

And then you can see here I mean you can select the images pretty well actually, and. I’m actually not too unhappy with this. What happens if I, what happens if I make a new one? Let’s just see. Yeah. It’s like, no. See how now it’s covering this? That’s not good. We don’t want that, right? 

So let’s do a little extra styling, so that when a user is editing it, we just want it to be easier to edit and not be covered. And then maybe when they’re not editing it, it could look the way it’s gonna look on the frontend.

Okay, so when we want styles that only go for the block editor experience, that’s in our editor.scss file, that’s part of our block. So let’s get rid of the dotted line. We don’t need that and we actually only want it when the block .is-selected. Or, and this is in a kind of an important one, if the block .has-child-selected.  Because we’re dealing with blocks inside of blocks and whether they’re focused on the kind of wrapper block or if they’re focused on either of the images, that’s the time we wanna apply the CSS. And so we really wanna make sure that we’re scoping it to that and we don’t need that. 

What I basically want, when we’re editing the block or editing the blocks inside, I just want it to be position:relative. I don’t want that absolute position. And then, you know what? Let’s do this display:grid and we’ll do two columns. So when I’m in there and I’m editing, just put the images next to each other. Don’t overlap them. No absolute position. And actually, let’s get rid of the scale too. Let’s get rid of the moving it around, any of that stuff- just put ’em right next to each other.

A grid, two columns, regular position, regular size. Okay, so we’ll save that. Let’s go back to the editor. 

.wp-block-create-block-example-inner-block {
	&.is-selected, &.has-child-selected{
		display: grid;
		grid-template-columns: 1fr 1fr;
			position: relative;
			transform: none;
			position: relative;
			transform: none;
}Code language: SCSS (scss)

Okay. I’ve reloaded the editor and we have our block. We will definitely have to deal with this too, I think, but let’s go to edit it and boom, they’re right next to each other. Nothing weird.

They’re not hard to get to. I’ve selected the image, but even if I select the parent, the kind of wrapper for both of them, that works well too. So, all of that’s pretty, pretty good. And then I come back over here and boom, they look good. So little editor styles right there. They won’t be applied on the front end. No CSS loading on the front end. It’s just on the back end. Perfect. 

Adding block supports

So there’s probably a few things that I would want to keep going on this, but let’s do one more thing. Let’s figure out: how do we deal with this situation [of the block overlapping other blocks on the page] because that obviously looks a little funky. What if I want to do some spacing or something like that?

So let’s go back to our block and let’s add some features to it. So we’re in our block.json. We’re gonna add a spacing option and we’re just gonna do, let’s just take margin and turn it true. And don’t add that comma, ’cause it’ll break my npm run start. And save. Everything is good. Okay, so what is this gonna do?

	"supports": {
		"html": false,
		"align": false,
		"spacing": {
			"margin": true
}Code language: JSON / JSON with Comments (json)

It’s gonna turn on the spacing features for that parent block that’s built into WordPress. As far as we know, it shouldn’t require us to do any code, anything like that. We should be able to just turn on a support and use it. So let’s see what else we can do, or let’s see if it works. 

Okay, so I’ve reloaded the page. So when I click, I often am selecting the image itself, which, you know, has all of the stuff that images have, you know, for example, you have your styles and things like that. That’s pretty cool. But if I go now to the parent block, there we go. Now we have some dimensions options here that we’ve turned on.

So, I guess that means top and bottom. Let’s find out. Oh, yep. All right, so let’s turn on the margins for the top and bottom. Let’s add a big margin like that. There we go. Now it’s added a little space to the top and bottom. That’ll probably also be nice in case the content stacks, you know, you kinda want that little space here.

I could hard code it, I could set it as a default in the block. There’s all sorts of options, but really I kind of just like having the setting turned on and just letting the user be able to kind of control that, because, you know, sometimes you get that situation where they actually want that overlap- they want that kind of effect. So it’s kind of nice to just use the core features to set that up.


But yeah, that’s essentially our block. Because it’s a block, there’s so many more things you could do. For example, in the image blocks, I can turn on block styles, right? And I could say, okay, make both of them round so that they have a cool kind of circle effect.

But, you know, block styles aren’t just limited to what’s here in our block.json file. We could actually define styles for the parent block. So I’ve done examples where I add a style that reverses which image is in front and which one’s in back, or a style that lets you change which direction the effect goes, or, or something like that, a setting to add shadows, all those sorts of things. So you can turn on block styles in your block.json file, and then all you have to do is write the CSS in here to support it. It’ll be scoped to your block perfectly. 

So that’s a quick little walkthrough of using inner blocks to take multiple blocks, put ’em together and make something new. What I really like about it is I spend almost no time writing JavaScript. I have one or two repos like this. In fact, I’ll probably use this repo in the future as a reference point. But you really just need to kind of make these few updates to edit and save, decide what blocks you need, decide what template is gonna look like.

And beyond that, it’s just writing a lot of CSS and getting to, you know, push the boundaries of that. And then always end with a little review of what it feels like to edit your block and make sure that that process feels good and smooth for your user. 

If you have any questions, post them in the comments below, and the full code for this will be available as a link below.

👉 Full Code Repo
👉 WordPress Create-Block Package
👉 Full tutorial on YouTube

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.