Learn WordPress Org’s build your own block course condensed notes

I’m going through wordpress.org’s build your own block course right now. Making notes here because there’s lots of stuff for me to forget.

Creating the block scaffold

Spin up your site, go to the wp-content/plugins folder and run the following command in a terminal (you need to have installed nodejs):

px @wordpress/create-block multi-columns

This will create the block scaffold for a new plugin called “multi-columns”.

The course notes that the files in the src directory are where you will make most of your changes.

Most of the files are pretty self-explanatory. Just note that “edit.js” controls what is seen in the editor, and “save.js” determines what will be show in the actual post.

Changes to the default

Note: make sure to this run inside the new plugin’s directory:

npm start

so you can see changes made in real time.

Also, make sure the new block is activated in WordPress plugins.

Change where the block appears in the editor

In block.json change category to design, and icon to columns.

Add interactivity in edit.js

Edit.js imports the hook useBlockProps. It’s required.

Change the <p> tag to a built-in gutenburg component called <RichText>. To add any component, follow three steps:

  1. edit block.json to direct data to the component and define attributes.
  2. edit edit.js to import the component, add it to the edit() function, and add an event handler
  3. edit save.js to import the component and add component to the save() function


add this at the same level as “name”:

"attributes": {
  "content": {
    "type": "string",
    "source": "html",
    "selector": "p"


include RichText in the inside the same brackets as “useBlockProps”.

Replace the return statement with:

return (
        { ...useBlockProps() }
        onChange={ onChangeContent }
        value={ attributes.content }
        placeholder="Enter some text here..."

Add { attributes, setAttributes } as a parameter to the Edit() function.

And add this const right inside the function:

const onChangeContent = ( val ) => {
    setAttributes( { content: val } );


Import RichText. Make the save function look like this:

export default function save( { attributes } ) {
    return (
            { ...useBlockProps.save() }
            value={ attributes.content }

Add some options to the block (background color, etc)

Add this to supports in block.json

"color": {},
"spacing": {
   "margin": true,
   "padding": true

Note that you need to fix the the tabs to make it valid JSON.

Add defaults to options

Again in block.json, a a style section to attributes (note that you need to add one, not use the style section that is already there):

"style": {
  "type": "object",
  "default": {
    "color": {
      "text": "#3a3a3a",
      "background": "#fbf9f4"
     "spacing": {
	"padding": {
	  "top": "20px",
	  "right": "20px",
	  "bottom": "20px",
	  "left": "20px"

Customization options

custom columns

Follow the three step process – block.json, edit.js, save.js


"columnCount": {
    "type": "integer",
    "default": 4


Need to pass columnCount to the useBlockProps

const { columnCount } = attributes;
const columnStyles = { columnCount };

and put it in the return:

        { ...useBlockProps( { style: columnStyles } ) }

Now, make it changeable, but need to limit the number of columns the user can select, so use the RangeControl component. First, import it:

import { RangeControl } from '@wordpress/components';

Then add it to the return statement in edit.js (note adding empty tags to group the two other tags:

            value={ columnCount }
            onChange={ onChangeColumnCount }
            min={ 2 }
            max={ 6 }

And add a change handler:

const onChangeColumnCount = ( val ) => {
    setAttributes( { columnCount: val } );

putting the controls on the side instead of in the block

First, InspectorControls. Import it from block-editor (with richtext and useblockprops), then wrap rangecontrol in it.

Then, import PanelBody from components to make it more beautiful. Wrap rangecontrol with it.


Just need to unpack the object to get the column number in save.js

export default function save( { attributes } ) {
  const { columnCount } = attributes;
  const columnStyles = { columnCount}
  return (
          { ...useBlockProps.save( {style: columnStyles}) }
          value={ attributes.content }  

More practice

adding styling to the columns



"columnWidth": {
		  "type": "integer",
		  "default": 200

add custom number input component (from the course) in a new folder called compenents. Then import it in


import NumberControl from './components/number-control';

add the variables in the edit() function

	const { columnCount, columnWidth } = attributes;
	const columnStyles = { columnCount, columnWidth 

And put width in the return (should be able to figure this part out).

!!! Be sure to cast the number to integer in the onchage functions with Number( val ) inside the function.

Then update the consts in save.js

Group panel controls

add a title to PanelBody:

<PanelBody title="Column Settings">

divider lines

Use SelectControl component

huge block in in return statement (it’s a select component, so you have to code all the options)

keep the panel closed at first:

<PanelBody title="Column Separator" initialOpen={false}>

Course does a pretty good job of making you repeat the process many times.

Inner blocks

Don’t need content section of block.json, because child blocks will handle it.

Get rid of RichText and use InnerBlocks instead.

Replace RichText with a div, it has useBlockProps as an… attribute of some kind. Then put innerblocks inside the div:

return (
       <div { ...useBlockProps( { style: columnStyles } ) }>
           <InnerBlocks />

Be sure to use InnerBlocks.Content in save.js

Control types of blocks allowed

const ALLOWED_BLOCKS = [ 'core/heading', 'core/paragraph', 'core/image' ];

Can also add a template:

<InnerBlocks allowedBlocks={ALLOWED_BLOCKS} template={MC_TEMPLATE} />


You can add keywords for easier searching for the block in the editor.

Make it full width by specifying that in block.json.

Make the interface translatable (to other languages) with the __( ) function.

Back in block.json, you can add a list of allowable parents.

Style varations: add in block.json, add in style.scss, create preview.

Custom styles

If you let user costomize a custom style you have created (in style.scss) you have to define the css tag in edit.js:

const columnStyles = {
    '--drop-cap-color': dropCapColor,

Use something like this to hide things when not needed:

{ attributes.className === 'is-style-drop-cap' && (
) }






Leave a Reply

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