Skip to content

Case study Tutorial

Teaching Gutenberg block development for Multidots, with my pets as test users

Multidots builds enterprise WordPress sites, and I pitched it 5 topics to build its authority with developers. I wrote all 5 and built every block in the 2 tutorials on Gutenberg, WordPress’s block editor. The second tutorial’s test users are 10 of my pets. WordPress newsletters picked up 3 of the 5.

Read the tutorial

Client
Multidots
Role
Technical Ghostwriter / WordPress Developer
Byline
Ghostwritten
Year
September 2024 to July 2025
Disciplines
Topic pitching, Release coverage, Tutorial design

Proof at a glance

What I pitched, wrote and built


I never had Multidots’ analytics, so every number here is one a reader can count on the published page.

  • 5

    topics I pitched and wrote

    all 5 published, September 2024 to July 2025

  • 89

    code blocks in the 2 block tutorials

    59 in the custom blocks tutorial, 30 in the dynamic one

  • 10

    of my pets in the test data

    3 Editors, 4 Authors, 3 Contributors

  • 3

    pieces in WordPress newsletters

    Gutenberg Times 320, wpMail 686 and 727

The problem

A developer who already knows the handbook learns nothing from a summary

Multidots wanted authority with WordPress developers. Those developers read the Block Editor Handbook, so the only piece worth writing was one that left them holding something the handbook doesn’t build for them.

Who they were for

Why Multidots needed tutorials a developer could finish

Enterprises hire Multidots for WordPress builds and migrations. I wrote for the developers on those teams first, since they’re the ones who’d judge whether the agency knows current WordPress.

They know the Block Editor Handbook already. Summarizing it would have been quicker, and a developer would have learned nothing they couldn’t read at the source.

So I wanted each tutorial to end with a block the reader had built and could use.

Pieces
5
Code blocks
100 across the 5
Contract
1

The rules

Rules that stayed fixed from the first piece to the fifth

Could a developer finish this with a working block and only this page open?

Multidots and I settled the terms once and they held for all 5 pieces: the reader, the voice, the stack, and where the code had to run.

The spec panel further down lists them. The three below are the ones that changed how a sentence got written.

What each piece had to do

A fresh local site
Every command had to work on a fresh local site, because developers paste commands straight into a terminal.
One setup, two tutorials
The dynamic blocks tutorial had to start where the custom blocks tutorial stopped, so a reader of both sets up their environment once.
The agency’s name in the code
Multidots’ name is in the code, from the multidots-cta slug to the multidots namespace. A reader who copies an example copies the agency’s name with it.

The five pieces

What each of the 5 pieces needed me to build

The pages went out under Multidots team bylines, so the names on them aren’t mine. I put my pets and my own humor into the pieces on purpose, to show I wrote them.

The first 3 in 12 days
September 13 to 25, 2024

The work

I pitched 5 topics, then built every block in them myself

They go from reading WordPress to building with it, and each one ends with something the reader has made.

How they were written

From a pitch to a block that runs

  1. Topics that go from reading WordPress to building with it

    Each of the 5 started as my pitch. I picked them because Gutenberg was what Multidots wanted to build its authority around.

    In September 2024, the first 3 went live within 12 days.

    The 404 guide covers 4 routes to a custom 404 page, from editing a classic theme’s 404.php file to installing a plugin. A reader could pick the one that fit their setup.

    Both block tutorials followed in February and July 2025. Between them, they have 89 of the 100 code blocks across the 5 pieces.

  2. A static block, taught through a cat’s guide to training humans

    The custom blocks tutorial starts from a terminal in the plugins folder and ends with a call-to-action (CTA) block. I scaffolded it with @wordpress/create-block and explained every file it generates.

    From there, the tutorial adds one feature at a time.

    Changing save() later breaks any copy of the block that a post already has. The screenshots show that error too, along with the Attempt recovery button that usually fixes it.

    Its last section reuses the same steps for a testimonials block and a pricing table.

    For the CTA itself, I wrote a cat’s guide to training humans. It starts with The Midnight Zoomies and The Strategic Meow.

    Squanchy, my first cat, gave the testimonial. She rated the service five paws up (her words).

    Stage, and what the block gains

    Scaffold
    a plugin that npm start rebuilds whenever a file changes
    edit.js and save.js
    one view for the editor and one for the published page
    RichText
    text a user can change, kept as an attribute
    Block supports
    color, spacing, and shadow controls from block.json
    Toolbar and sidebar
    BlockControls, InspectorControls, and PanelBody
    InnerBlocks
    nested core blocks, a 2-column template, and a lock
    parent and i18n
    a rule for where it can go, and strings a translator can reach
  3. Moving the output to PHP for a block that reads live data

    The dynamic blocks tutorial opens with a link back to the custom blocks tutorial, which covered static blocks only.

    Its block lists a site’s users by role, so the list has to change whenever someone joins or leaves. That means WordPress renders it on the server when a page loads.

    I scaffolded it with the dynamic variant of create-block, which swaps save.js for render.php. In that file, a WP_User_Query fetches whoever matches the choice in the block’s sidebar.

    The block editor needed the same list, and the tutorial builds it twice.

    To test the block, the tutorial says “we’ve added a bunch of fake users.” They’re 10 of my pets, sorted into Editors, Authors, and Contributors, with their photos as avatars.

    You’ll find the code in my users-block repo. It also adds a setting the tutorial doesn’t have, for how many users to show.

    The editor preview, built twice

    ServerSideRender
    asks the server to rerun the PHP file after any change
    What it costs
    a round trip per change, and a block that looks broken if the request fails
    useEntityRecords from @wordpress/core-data
    requests the users from the REST API and renders them in the browser
    What it costs
    a second copy of the markup to keep in step with the PHP file
  4. Checking my work before a reader tried it

    I applied Multidots’ basic feedback on the drafts before the pieces went live.

    The check, what I did, and why

    Build it first
    ran the commands and built both blocks locally
    Why
    the code had worked at least once before anyone copied it
    Screenshot each state
    captured what the screen should look like after a step, errors included
    Why
    a reader can compare their screen with mine
  5. Dating the work from the lockfiles

    You can check my build dates in either repo. Its package-lock.json file records the build tool’s version on the day I installed it.

    So you know both blocks existed before Multidots published the tutorials.

    What each lockfile records

    CTA, @wordpress/scripts
    30.9.0, npm’s newest release from January 15–29, 2025
    CTA, tutorial went live
    February 14, 2025
    Users, @wordpress/scripts
    30.18.0, npm’s newest release from June 4–25, 2025
    Users, tutorial went live
    July 15, 2025
  6. The decision the tutorial should have opened with

    Birgit Pauli-Haack has curated Gutenberg Times, a weekly roundup of block editor news, since 2017. Automattic sponsors her work on WordPress as a developer advocate. Her March 8, 2025 edition walked through my custom blocks tutorial, from setting up the environment to the testimonials block.

    Then she named what it missed, which was when to use block styles or variations before building a block from scratch.

    She was right. My tutorial brings them up once, in its last section, as something to add after the block exists.

    For instance, a developer who only needed a second button style could follow the tutorial to the end and build a block they didn’t need.

    If I wrote it today, it would open with that decision, before the first command.

    What to reach for first

    A core block
    when its own settings do the job
    A block style
    for a new look on an existing block
    A block variation
    for a preset version of one
    A pattern
    for a group of blocks the reader reuses
    A custom block
    for everything left over

What stayed fixed

The settings all 5 pieces ran under

One contract, one local site, one voice, and a stack that moved from WordPress 6.6 to 6.8.

client
Multidots, an enterprise WordPress agency and VIP Gold partner
readers
WordPress developers first, then the people who hire agencies
voice
Multidots’ “we,” under team bylines
stack
WordPress 6.6 to 6.8, create-block, wp-scripts, React, PHP
local site
multidots-demo.test named once, in the 404 guide’s code
feedback
basic notes from Multidots, which I applied
published
September 2024 to July 2025, on 1 contract

What happened

Newsletters picked up 3 of the 5, and one named what I skipped

Gutenberg Times walked a reader through the custom blocks tutorial and then named the decision it never made, which was when to build a block at all.

The findings

Where the pieces turned up after Multidots published them

Two WordPress newsletters and one weekly roundup carried 3 of the 5, and one of them walked a reader through my tutorial.

All 5 pieces are still up, and their pages show 2026 update dates.

What this page doesn’t claim

  • Traffic, rankings, or leads. I never had access to Multidots’ analytics.
  • Authorship on the pages. They show Multidots team bylines, and 2 of the 2024 pieces changed bylines after publication.
  • Multidots’ 2026 updates to the 5 pieces.
  • The CTA’s source code on GitHub. The repo I set up for it has the users block’s code, as the next section explains.

The blocks I built

A cat that trains humans, and 10 pets with WordPress roles


Every screenshot in both tutorials came off my own local site, and these three are the blocks at the end of each build.

  • February 2025 The CTA block from the custom blocks tutorial, as published in February 2025. Read the tutorial
  • February 2025 Squanchy’s testimonial, from the same tutorial. Read the tutorial
  • July 2025 The users block with its test data, 10 of my pets sorted by role, from the dynamic blocks tutorial. Read the tutorial

Then and now

The custom blocks tutorial, in March 2025 and today

Multidots edited all 5 pages in 2026, so the live versions are not quite the ones I filed. The captures in Sources are.

What has not moved is the teaching: the tutorial still runs to the same 59 code blocks, in the same order, under the same byline.

Both columns were read off the March 28, 2025 capture and the live page, September 19, 2026.

What I got wrong

Errors I made, and the check I’d add first

Showing a site’s current users is the point of the dynamic blocks tutorial, and its editor preview has 3 problems.

It shows nothing while the data loads. The handbook’s own example prints Loading, then No Posts if the list comes back empty, so an editor using my block can’t tell a slow request from a broken one.

Anyone with the Editor role gets a blank block and no error. core-data asks for users in edit context, which only administrators can read by default.

It prints a user’s nickname where render.php prints display_name. Anyone who set those differently shows up under 2 names.

Reading the pieces line by line, I found 32 more errors of 7 kinds. I’ve put one of each below, and the download lists them all.

I also mixed up my own repos on July 13, 2025, in the space of 7 minutes. At 07:58 UTC I added a license to custom-gutenberg-block. At 07:59 I uploaded the CTA plugin’s multidots-cta.php, package.json, and readme to it. At 08:03 I uploaded the users block to a second repo, users-block. At 08:05 I uploaded the users block’s src and build folders into the CTA repo by mistake.

The CTA’s own code never made it to GitHub. You can see the block at each stage in the tutorial’s screenshots, and the lockfile in the repo dates my work to January 2025.

Multidots wanted developers to trust it with their WordPress sites. For a tutorial, that trust depends on whether the reader ends up with a working block, so I built every example myself.

The published pages needed the same test the code got. Before your next tutorial goes live, follow it from the page itself on a fresh site, pasting each snippet as you go. Then log in with the Editor role and open the block again.

It’s the check I’d add first. It would have caught the wrong pricing command and the unquoted text domain that crashes the testimonials block. The Editor login would have shown the blank preview.

Code that fails when copied
  • The pricing table scaffolds with multidots-testimonials, the testimonials block’s name.
  • 4 of them.
Code that does the wrong thing
  • edit.js uses the text domain user-blocks where block.json says users-block, so those strings never translate.
  • 6 of them.
A step that risks the site
  • Editing the active theme’s 404.php in the Theme File Editor, which the next theme update overwrites.
  • 1 of them.
JSON that won’t parse
  • A theme.json sample with "link": actual in it.
  • 2 of them.
Names and terms spelled wrong
  • @wordpress/core-date, “Reach Hook,” and ServerSideRequest.
  • 15 of them.
Loose terms
  • “backend” for the block editor, which a developer reads as the server.
  • 3 of them.
A passage printed twice
  • The 6.6 post prints the same two sentences about the unified publish flow twice in a row.
  • 1 of them.

One error of each kind, with its count:

Sources and credits

Versions I checked, and the people I credit

The live pages include Multidots 2026 updates and re-uploaded images, so the captures are the versions to check.