MW
Tutorial

Block Attributes and Serialization

Master the attribute system — types, sources, defaults, and how block data round-trips between the editor, the database, and the front-end.

intermediate 30 min May 13, 2026
A working custom block from Part 3

BLOCK ATTRIBUTES AND SERIALIZATION

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.

SourceWhen to useStores in
(none)Most cases — simple, fast, fully controlledBlock comment JSON
attributeReading from an HTML attribute (e.g., href, src)The HTML attribute
htmlStoring rich text content directly in the HTMLElement innerHTML
textStoring plain text (no formatting)Element textContent
meta (deprecated)Reading from post_metawp_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.

You've completed this tutorial!

Get the next one in your inbox. Practical tips, no fluff.

Subscribe

Get weekly notes in your inbox

Practical tips, tutorials and resources. No spam.