The AI Text block from Lesson 2 already has a mode attribute, 'rewrite' by default,
that the toolbar and summarize logic already understand. This lesson uses that
attribute to register a second, pre-configured entry in the inserter, “AI Summary
Block”, without writing a second block from scratch. It’s also the right place to be
direct about something developers sometimes go looking for: there is no separate
slash-command API in WordPress distinct from block registration and variations.
Lesson 2’s “AI Text” block (my-plugin/ai-text), specifically its mode attribute,
already needs to exist. This lesson adds a variation on top of that registration, it
doesn’t build a new block.
Step 1: A variation in block.json
Block variations let one registered block type show up as multiple, differently
labeled entries in the inserter, each with its own default attributes. Add a
variations array to the AI Text block’s existing block.json:
// File: my-plugin/blocks/ai-text/block.json
{
"$schema": "https://schemas.wp.org/trunk/block.json",
"apiVersion": 3,
"name": "my-plugin/ai-text",
"title": "AI Text",
"category": "text",
"icon": "editor-spellcheck",
"attributes": {
"content": { "type": "string", "source": "html", "selector": "p", "default": "" },
"mode": { "type": "string", "default": "rewrite" }
},
"variations": [
{
"name": "ai-summary",
"title": "AI Summary Block",
"description": "A block pre-configured to summarize its text with AI instead of rewriting it.",
"icon": "excerpt-view",
"attributes": { "mode": "summarize" },
"isActive": [ "mode" ],
"scope": [ "inserter" ]
}
],
"textdomain": "my-plugin",
"editorScript": "file:./index.js"
}
scope: ["inserter"] means this variation only shows up as its own inserter entry, it
doesn’t add a variation-picker UI on top of an already-placed block, which is the right
choice here since switching a block between “rewrite” and “summarize” is just a mode
attribute the block’s own toolbar already lets an author change (Lesson 2’s button
label switches based on it). isActive: ["mode"] tells WordPress to treat an existing
block instance as matching this variation whenever its mode attribute equals
"summarize", which is what determines whether the inserter shows this block as
already placed versus available to insert again.
Step 2: The alternative, registerBlockVariation()
If you need to add a variation to a block from separate plugin code, your own plugin
adding a variation to a block registered elsewhere, rather than in that block’s own
block.json, use registerBlockVariation() imperatively:
// File: my-plugin/src/register-summary-variation.js
import { registerBlockVariation } from '@wordpress/blocks';
registerBlockVariation( 'my-plugin/ai-text', {
name: 'ai-summary',
title: 'AI Summary Block',
description: 'A block pre-configured to summarize its text with AI instead of rewriting it.',
icon: 'excerpt-view',
attributes: { mode: 'summarize' },
isActive: [ 'mode' ],
scope: [ 'inserter' ],
} );
Both approaches produce the same result, the block.json array is the right choice
when you own the block being varied, registerBlockVariation() is the right choice
when you’re adding a variation to a block you don’t own, the same relationship Lesson 6
had between owning a block and only being able to filter one.
Step 3: There is no separate slash-command API
It’s a reasonable thing to go looking for: some way to register “when someone types
/ai-summary, insert this specific configuration” as its own distinct system. It
doesn’t exist as a separate API. The inserter’s search-as-you-type list and the /
slash-command shortcut inside the editor’s text areas are both reading from the exact
same registered block types and variations, there’s nothing extra to wire up beyond
what Steps 1 and 2 already did. Give a variation a clear title and keywords
(an optional array of extra search terms) and it becomes reachable both ways for free.
Inserter visibility more broadly comes from two places: whether a block type is
registered and allowed at all, controllable server-side with the
allowed_block_types_all PHP filter, and which variations of an allowed block type
exist, controlled by the registration covered in Steps 1 and 2. The
allowed_block_types_all filter operates at the whole-block-type level, it can hide
my-plugin/ai-text entirely, but it has no concept of hiding one variation while
allowing another, that distinction lives entirely in how you register variations, not
in that filter.
If isActive doesn’t correctly reflect the attribute that distinguishes your
variation, an already-placed “AI Summary Block” can show up in the block list or block
switcher looking indistinguishable from the plain “AI Text” block, since WordPress has
no way to tell which registered variation a given instance is supposed to represent.
Keep isActive pointed at whichever attribute (or attributes) actually differ between
variations, mode in this case.
Test it
Rebuild your block assets, open the post editor, open the inserter, and search for
“summary”. You should see “AI Summary Block” appear as its own entry, separate from
“AI Text”, with its own icon and description. Insert it and confirm the block’s
toolbar button (from Lesson 2) already reads “Summarize with AI” without you touching
the mode attribute manually, confirming the variation’s default attributes applied
correctly on insert. Then try typing /summary inside an empty paragraph, the same
variation should appear in the slash-command list.
Recap
A block variation, registered either inside block.json’s variations array or
imperatively with registerBlockVariation(), gives one block type multiple,
independently discoverable entries in the inserter, each with its own default
attributes, isActive telling WordPress how to recognize an already-placed instance of
that variation. There is no separate API for slash commands specifically, the inserter
search and the / shortcut both read from the same block and variation registrations,
and the allowed_block_types_all PHP filter controls whole block types, not individual
variations within one.
Resources & further reading
- Block Variations API reference, developer.wordpress.org
- Block API reference: block.json, developer.wordpress.org
- @wordpress/blocks package reference, developer.wordpress.org