Attributes are the data your block stores. In Part 3 we declared three: title, body, calloutType. This tutorial covers how they actually work.
Attribute types
The supported type values are: string, number, integer, boolean, array, object, null. Use the most specific type — it drives validation and the editor’s behavior.
"attributes": {
"title": { "type": "string", "default": "Heads up" },
"dropCap": { "type": "boolean", "default": false },
"fontSize": { "type": "integer", "default": 16 },
"items": { "type": "array", "default": [] },
"metadata": { "type": "object", "default": {} }
}
Attribute sources
source tells WordPress where in the saved HTML to find an attribute. Without a source, the attribute is stored in the block comment as JSON. With a source, it’s extracted from the rendered HTML on parse.
| Source | When to use | Stores in |
|---|---|---|
| (none) | Most cases — simple, fast, fully controlled | Block comment JSON |
attribute | Reading from an HTML attribute (e.g., href, src) | The HTML attribute |
html | Storing rich text content directly in the HTML | Element innerHTML |
text | Storing plain text (no formatting) | Element textContent |
meta (deprecated) | Reading from post_meta | wp_postmeta table |
"linkUrl": {
"type": "string",
"source": "attribute",
"selector": "a",
"attribute": "href"
}
That declaration tells WordPress: “the linkUrl attribute value is the href of the <a> element in this block’s saved HTML.”
Why sources matter
Without a source, your block comment looks like this:
<!-- wp:my-plugin/callout {"title":"Heads up","body":"Body text"} -->
<div class="my-callout">
<h4 class="my-callout-title">Heads up</h4>
<p class="my-callout-body">Body text</p>
</div>
<!-- /wp:my-plugin/callout -->
The attributes are in the comment AND duplicated in the HTML. That’s fine — slightly redundant but simple.
With sources, attributes are NOT in the comment — only in the HTML:
<!-- wp:my-plugin/callout -->
<div class="my-callout">
<h4 class="my-callout-title">Heads up</h4>
<p class="my-callout-body">Body text</p>
</div>
<!-- /wp:my-plugin/callout -->
Smaller post_content, single source of truth. Worth doing for blocks where the HTML is the canonical representation.
Defaults matter
When a user inserts a fresh block, the editor uses the default value for each attribute. Set sensible defaults so the inserted block isn’t a sea of “Untitled” placeholders.
Deprecations — evolving attributes safely
When you change a Save function or attribute schema, existing saved content stops validating. To keep it working, register a deprecation:
import { registerBlockType } from '@wordpress/blocks';
registerBlockType(metadata.name, {
edit: Edit,
save: Save,
deprecated: [
{
attributes: oldAttributes,
save: OldSave,
migrate: (oldAttributes) => ({
...oldAttributes,
// map old shape → new shape
calloutType: oldAttributes.style ?? 'info',
}),
},
],
});
The deprecation array lets WordPress recognize old saved HTML as a valid (but old) version of the block, then migrate it forward when the user re-edits.
Verification
In the editor, switch to Code editor (top-right menu in wp-admin) and inspect your block’s serialized HTML. Confirm attributes show up where you expect them — comment vs HTML.
What’s next
Part 5 adds inspector controls (the sidebar panel) so users can change calloutType without typing into the block.
Get the next one in your inbox. Practical tips, no fluff.
More tutorials
View all-
Block Patterns
Register reusable block compositions — landing-page hero, feature grid, FAQ section — that users insert as a complete unit.
25 min
-
Building a Block Plugin
Package your block as a proper WordPress plugin — file structure, asset enqueueing, server-side render callbacks, plugin metadata.
35 min
-
Building Your First Custom Block
Replace the scaffolded placeholder with a real custom Callout block — editable title, body, and a type selector — using registerBlockType + edit/save.
45 min