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- WordPress
How to Prepare Your Plugin or Theme for WordPress 7.1
A WordPress 7.1 compatibility checklist: iframed editor, client-side media hooks, Abilities public flag, jQuery UI 1.14.2, plus features that did not ship (React 19, collaboration).
22 min
- WordPress
What's New in WordPress 7.1 for Developers
WordPress 7.1 field guide for developers: client-side media, Abilities API, always-iframed editor, SVG Icon API, global styles, and what to test before users upgrade.
18 min
- WordPress
Abilities API Changes in WordPress 7.1
WordPress 7.1 upgrades the Abilities API: a unified public flag, wp_get_abilities() filtering, execution lifecycle hooks, JSON Schema for clients, and typed REST input.
25 min