---
title: "How I build Gutenberg blocks with AI agents"
author: "Zakaria Binsaifullah"
url: "https://devzakaria.com/blog/ai-agents-gutenberg-blocks"
published: "2026-10-13"
updated: "2026-10-13"
tags: "gutenberg, wordpress, ai"
---

# How I build Gutenberg blocks with AI agents

Agents scaffold the block, write the boilerplate and the first tests. I write the editor experience, the markup decisions and the review. Here is the workflow, step by step.

I have been building Gutenberg blocks since the block editor shipped, and leading development on block plugins for the last few years. Today most of my blocks start with AI agents. Not because the agents are better at WordPress than I am — they aren't — but because a block has a lot of predictable code around a small amount of code that really matters.

This post is the workflow I actually use: what I hand to the agents, what I keep for myself, and the checklist every block goes through before it ships.
## The split: boring code vs. decisions

A typical custom block touches the same files every time:

- `block.json` — name, attributes, supports, scripts and styles
- `edit.tsx` — the editor UI: inspector controls, toolbar, placeholders
- `save.tsx` (or a `render.php` for dynamic blocks)
- styles for the editor and the front end
- a PHP file to register it, and usually a test or two

Most of that is structure, not judgement. Agents are fast and reliable at structure. Where they are weak is exactly where blocks succeed or fail: _how the block feels to edit_, what markup it leaves behind, and whether it still works the day you change it.

So the rule is simple: **agents write the scaffolding and the first draft, I write the decisions, and nothing ships until I have read every line.**
## Step 1: a brief the agents can't misread

Every block starts as a short written brief. Not a chat message — a file. It answers:

1. **Who edits this block, and what do they change most?** A pricing card gets its plan name, price and features edited constantly; its layout almost never.
2. **Static or dynamic?** If the output depends on data that changes (posts, products, prices), it renders on the server. Otherwise it is saved markup.
3. **What does the front end output look like?** I write the target HTML by hand. This is the single most useful thing in the brief — it stops agents from inventing wrapper divs.
4. **What must be accessible?** Headings, focus order, labels, colour contrast in both themes.

A planner agent turns that brief into tasks. I read the task list before anything gets built. This is where most mistakes are cheapest to fix.
## Step 2: the agents scaffold

The builder agent creates the block from the plan. For a pricing card the attributes end up something like this:

```json
{
  "apiVersion": 3,
  "name": "dz/pricing-card",
  "title": "Pricing card",
  "category": "design",
  "attributes": {
    "plan": { "type": "string", "source": "html", "selector": "h3" },
    "price": { "type": "string", "default": "$49" },
    "features": { "type": "array", "default": [] },
    "highlighted": { "type": "boolean", "default": false }
  },
  "supports": { "color": { "background": true, "text": true }, "spacing": { "padding": true } }
}

```

And a first pass at the editor component:

```tsx
export default function Edit({ attributes, setAttributes }) {
  const { plan, price } = attributes;
  const blockProps = useBlockProps({ className: 'pricing-card' });

  return (
    <div {...blockProps}>
      <RichText tagName="h3" value={plan} onChange={(plan) => setAttributes({ plan })} placeholder="Plan name" />
      <RichText tagName="p" className="price" value={price} onChange={(price) => setAttributes({ price })} />
    </div>
  );
}

```

That gets me from an empty folder to a block that registers, renders and saves in minutes. It is not finished — it is a starting point I don't have to type.
## Step 3: the parts I write by hand

This is the work clients are actually paying for.

**The editing experience.** Agents tend to put every option in the sidebar. I move the things people change constantly into the canvas (inline `RichText`, toolbar buttons) and leave rare settings in the inspector. A block should be editable without hunting.

**The saved markup.** I compare the output against the HTML I wrote in the brief, element by element. Extra wrappers, inline styles and missing semantic tags get removed now, because changing saved markup later means writing deprecations.

**Attributes that will survive.** Once a block is in use, renaming an attribute or changing its source breaks existing content. I decide the attribute shape deliberately and add a `deprecated` entry the moment I change anything that has shipped.

**Theme and editor parity.** I check the block in the editor and on the front end with real theme styles, in light and dark, on mobile. Block supports (colour, spacing, typography) should do the work instead of custom controls wherever possible.
## Step 4: review — every change, every time

The reviewer agent runs linting, builds and the tests, and flags obvious problems. Then I review the diff myself with a short checklist:

- Does the saved markup match the brief? No surprise wrappers or inline styles.
- Can a keyboard user reach and edit everything? Are headings in order?
- Does it validate after a reload? (Block validation errors are the classic Gutenberg bug.)
- Are strings translatable and escaped on output?
- Does it still work with the theme's colours, fonts and spacing?
- Is there anything here I wouldn't be happy to maintain in a year?

Anything that fails goes back as a specific change request — "remove the wrapper div, use the `h3` from the brief" — not "make it better". Agents respond well to precise feedback and badly to vague feedback, which is true of most people too.
## What this changes for clients

The honest summary: the scaffolding that used to take a morning now takes minutes, so more of the budget goes into the editing experience, accessibility and the details that make a block pleasant to use. The quality bar doesn't move, because the review step doesn't move.

If you need a custom block — or a whole block plugin — and want it built this way, [book a 20-minute call](/contact#book) or [send me a short brief](/brief).
