Block Attributes
Attributes define the data structure for your block. They determine what gets saved and how values are extracted from saved content.
Attribute Definition
Each attribute is defined with a type, optional source, and optional default:
{
"attributes": {
"title": {
"type": "string",
"default": ""
},
"count": {
"type": "number",
"default": 5
}
}
}
Attribute Types
string
Text values:
{
"content": {
"type": "string",
"default": ""
}
}
number
Numeric values (integers or floats):
{
"columns": {
"type": "number",
"default": 3
}
}
boolean
True/false values:
{
"isVisible": {
"type": "boolean",
"default": true
}
}
object
Complex nested data:
{
"settings": {
"type": "object",
"default": {
"showTitle": true,
"showDate": false
}
}
}
array
Lists of values:
{
"items": {
"type": "array",
"default": [],
"items": {
"type": "object"
}
}
}
Array with typed items:
{
"tags": {
"type": "array",
"items": {
"type": "string"
},
"default": []
}
}
integer
Whole numbers only:
{
"postId": {
"type": "integer"
}
}
null
Explicitly nullable:
{
"selectedItem": {
"type": [ "object", "null" ],
"default": null
}
}
Attribute Sources
Sources define how attribute values are extracted from saved HTML content.
No Source (Block Comment)
When no source is specified, the value is stored in the block’s HTML comment delimiter:
{
"mediaId": {
"type": "number"
}
}
Saved HTML:
<!-- wp:my-plugin/block {"mediaId":123} -->
<div class="wp-block-my-plugin-block">...</div>
<!-- /wp:my-plugin/block -->
Use for: IDs, configuration options, non-visible data.
html
Extracts innerHTML from an element:
{
"content": {
"type": "string",
"source": "html",
"selector": "p"
}
}
Saved HTML:
<!-- wp:my-plugin/block -->
<div class="wp-block-my-plugin-block">
<p>This content is extracted</p>
</div>
<!-- /wp:my-plugin/block -->
With multiline for multiple elements:
{
"content": {
"type": "string",
"source": "html",
"selector": "ul",
"multiline": "li"
}
}
text
Extracts text content (strips HTML):
{
"plainText": {
"type": "string",
"source": "text",
"selector": ".title"
}
}
attribute
Extracts an HTML attribute value:
{
"url": {
"type": "string",
"source": "attribute",
"selector": "a",
"attribute": "href"
}
}
{
"imageUrl": {
"type": "string",
"source": "attribute",
"selector": "img",
"attribute": "src"
}
}
{
"altText": {
"type": "string",
"source": "attribute",
"selector": "img",
"attribute": "alt"
}
}
query
Extracts an array of values from multiple elements:
{
"images": {
"type": "array",
"source": "query",
"selector": ".gallery-item",
"query": {
"url": {
"type": "string",
"source": "attribute",
"selector": "img",
"attribute": "src"
},
"alt": {
"type": "string",
"source": "attribute",
"selector": "img",
"attribute": "alt"
},
"caption": {
"type": "string",
"source": "html",
"selector": "figcaption"
}
}
}
}
Saved HTML:
<div class="gallery">
<figure class="gallery-item">
<img src="image1.jpg" alt="First">
<figcaption>Caption 1</figcaption>
</figure>
<figure class="gallery-item">
<img src="image2.jpg" alt="Second">
<figcaption>Caption 2</figcaption>
</figure>
</div>
Extracted value:
[
{ url: "image1.jpg", alt: "First", caption: "Caption 1" },
{ url: "image2.jpg", alt: "Second", caption: "Caption 2" }
]
meta (Deprecated)
Links to post meta (deprecated, use block bindings instead):
{
"metaValue": {
"type": "string",
"source": "meta",
"meta": "my_meta_key"
}
}
Requires meta registration:
register_post_meta( 'post', 'my_meta_key', [
'show_in_rest' => true,
'single' => true,
'type' => 'string',
] );
Selectors
Selectors determine which HTML element to extract from:
{
"title": {
"type": "string",
"source": "html",
"selector": "h2.block-title"
}
}
Selector types:
- Element:
"p","h2","img" - Class:
".my-class" - ID:
"#my-id" - Attribute:
"[data-type]" - Combined:
"div.content > p:first-child"
Important: If multiple elements match, only the first is used (except with query).
Default Values
Set default values for attributes:
{
"columns": {
"type": "number",
"default": 3
},
"alignment": {
"type": "string",
"default": "center"
},
"items": {
"type": "array",
"default": []
},
"settings": {
"type": "object",
"default": {
"showImages": true,
"maxItems": 10
}
}
}
Enum Validation
Restrict values to specific options:
{
"size": {
"type": "string",
"enum": [ "small", "medium", "large" ],
"default": "medium"
}
}
Role Attribute
Identify special attributes for WordPress features:
{
"content": {
"type": "rich-text",
"source": "rich-text",
"selector": "p",
"__experimentalRole": "content"
}
}
Roles:
content– Block’s main content (for synced patterns)style– Style-related dataother– General configuration
Rich Text Source
For RichText content (WordPress 6.5+):
{
"content": {
"type": "rich-text",
"source": "rich-text",
"selector": "p"
}
}
This preserves formatting data better than html source.
Working with Attributes in JavaScript
Reading Attributes
import { useBlockProps } from '@wordpress/block-editor';
export default function Edit( { attributes } ) {
const { title, count, isVisible } = attributes;
return (
<div { ...useBlockProps() }>
<h2>{ title }</h2>
<p>Count: { count }</p>
</div>
);
}
Setting Attributes
export default function Edit( { attributes, setAttributes } ) {
const { title } = attributes;
return (
<div { ...useBlockProps() }>
<TextControl
label="Title"
value={ title }
onChange={ ( newTitle ) => setAttributes( { title: newTitle } ) }
/>
</div>
);
}
Setting Multiple Attributes
setAttributes( {
title: 'New Title',
count: 5,
isVisible: true,
} );
Setting Nested Object Attributes
const { settings } = attributes;
setAttributes( {
settings: {
...settings,
showTitle: false,
},
} );
Setting Array Attributes
const { items } = attributes;
// Add item
setAttributes( {
items: [ ...items, { id: Date.now(), text: 'New item' } ],
} );
// Remove item
setAttributes( {
items: items.filter( ( item ) => item.id !== idToRemove ),
} );
// Update item
setAttributes( {
items: items.map( ( item ) =>
item.id === idToUpdate ? { ...item, text: 'Updated' } : item
),
} );
PHP Access
In render.php or render_callback:
<?php
// $attributes is available automatically in render.php
$title = $attributes['title'] ?? '';
$count = $attributes['count'] ?? 3;
$items = $attributes['items'] ?? [];
echo '<h2>' . esc_html( $title ) . '</h2>';
echo '<p>Count: ' . absint( $count ) . '</p>';
foreach ( $items as $item ) {
echo '<li>' . esc_html( $item['text'] ) . '</li>';
}
Complete Example
{
"attributes": {
"mediaId": {
"type": "number"
},
"mediaUrl": {
"type": "string",
"source": "attribute",
"selector": "img",
"attribute": "src"
},
"title": {
"type": "string",
"source": "html",
"selector": ".card-title"
},
"description": {
"type": "string",
"source": "html",
"selector": ".card-description"
},
"linkUrl": {
"type": "string",
"source": "attribute",
"selector": "a.card-link",
"attribute": "href"
},
"linkTarget": {
"type": "string",
"source": "attribute",
"selector": "a.card-link",
"attribute": "target"
},
"features": {
"type": "array",
"source": "query",
"selector": ".feature-item",
"query": {
"icon": {
"type": "string",
"source": "attribute",
"selector": "svg",
"attribute": "data-icon"
},
"text": {
"type": "string",
"source": "html",
"selector": "span"
}
}
},
"layout": {
"type": "string",
"enum": [ "horizontal", "vertical" ],
"default": "vertical"
},
"showFeatures": {
"type": "boolean",
"default": true
}
}
}
Save component:
export default function Save( { attributes } ) {
const {
mediaUrl,
title,
description,
linkUrl,
linkTarget,
features,
showFeatures,
} = attributes;
return (
<div { ...useBlockProps.save() }>
{ mediaUrl && <img src={ mediaUrl } alt="" /> }
<h3 className="card-title">{ title }</h3>
<p className="card-description">{ description }</p>
{ showFeatures && (
<ul>
{ features.map( ( feature, index ) => (
<li key={ index } className="feature-item">
<svg data-icon={ feature.icon } />
<span>{ feature.text }</span>
</li>
) ) }
</ul>
) }
{ linkUrl && (
<a className="card-link" href={ linkUrl } target={ linkTarget }>
Learn More
</a>
) }
</div>
);
}
Best Practices
-
Use appropriate sources: Store visible content in HTML (with sources), configuration in block comment (no source).
-
Provide defaults: Always set sensible defaults, especially for new attributes.
-
Keep it flat: Avoid deeply nested objects when possible.
-
Type correctly: Use
numberfor numbers,booleanfor flags,arrayfor lists. -
Validate with enum: Restrict string values when there’s a finite set of options.
-
Consider migrations: When changing attribute structure, add deprecations.