Code

Build a custom Gutenberg block from scratch

A full register_block_type walkthrough that covers block.json, edit/save components, server-side rendering, and the escaping judgment call most tutorials skip.

Works with Claude / GPT1,850 uses 4.5

The prompt

code-custom-gutenberg-block-from-scratch
I need a custom "Testimonial" block for a client site. It should have:
- An author name (text)
- A quote (rich text, multi-line)
- An optional avatar image
- A "featured" toggle that adds a highlighted border style

ENVIRONMENT: WordPress 6.7, using @wordpress/scripts for the build, block should be
registered from a plugin (not a theme), and needs to work in both the post editor and
full site editing templates.

Build this like a developer who's shipped several blocks to production, in order:
1. Generate the `block.json` with correct `apiVersion: 3`, attributes for each field
   above (name, quote, avatarUrl, featured), and appropriate `supports` (at minimum
   color and spacing, since design will ask for it eventually).
2. Write `edit.js` using `RichText`, `MediaUpload`, and `ToggleControl` from
   `@wordpress/block-editor` and `@wordpress/components`, wired to the attributes.
3. Decide and justify: should `save.js` output static markup, or should this be a
   dynamic block with a PHP `render_callback`? Since the featured border style might
   later depend on a site option (e.g. brand color from theme settings), recommend
   dynamic rendering and show the PHP side using `register_block_type()` reading
   `block.json`, with every attribute properly escaped (`esc_html`, `esc_url`,
   `esc_attr`) rather than trusting stored post content.
4. Flag the one mistake that causes "block validation failed, attempting recovery"
   errors down the line: a mismatch between what `save.js` outputs and what's stored,
   usually from someone editing `save.js` after content already exists in posts. Explain
   how to handle that migration safely with a `deprecated` entry instead of breaking
   existing content.

Adjust the fields and attributes to match my actual block.

Dynamic blocks with a render_callback are the safer default for anything client-editable, since you can change markup later without corrupting saved content. If you want a faster first draft of the block scaffold before refining it by hand, CodeWP generates a reasonable starting point for the JS/PHP split.