Skip to main content
Block attributes provide the structured data needs of a block. They define how data is stored and extracted from saved content.

Attribute definition

Attributes are specified in an object where each key is the attribute name:
attributes: {
	url: {
		type: 'string',
		source: 'attribute',
		selector: 'img',
		attribute: 'src'
	},
	title: {
		type: 'string'
	},
	size: {
		enum: [ 'large', 'small' ]
	}
}

Attribute schema

Each attribute definition must include either type or enum, and may include additional fields.

Type validation

The type field indicates the data type. Must be one of:
  • null
  • boolean
  • object
  • array
  • string
  • integer
  • number (same as integer)
{
	count: {
		type: 'number'
	},
	enabled: {
		type: 'boolean'
	},
	metadata: {
		type: 'object'
	}
}

Enum validation

Restricts values to a fixed set:
size: {
	enum: [ 'large', 'small', 'tiny' ]
}

Value sources

The source field determines where attribute data is stored.

No source (comment delimiter)

Without a source, data is stored in the block’s comment delimiter:
title: {
	type: 'string'
}
Saved as:
<!-- wp:my-block {"title":"hello world"} -->

attribute source

Extracts values from HTML element attributes:
url: {
	type: 'string',
	source: 'attribute',
	selector: 'img',
	attribute: 'src'
}
Given this HTML:
<div>
	<img src="https://example.com/image.jpg" />
</div>
Returns:
{ "url": "https://example.com/image.jpg" }

Boolean attributes

For existence checks like disabled:
disabled: {
	type: 'boolean',
	source: 'attribute',
	selector: 'button',
	attribute: 'disabled'
}

text source

Extracts inner text using textContent:
content: {
	type: 'string',
	source: 'text',
	selector: 'figcaption'
}
Given:
<figure>
	<figcaption>The caption text</figcaption>
</figure>
Returns:
{ "content": "The caption text" }

html source

Extracts inner HTML using innerHTML:
content: {
	type: 'string',
	source: 'html',
	selector: 'figcaption'
}
Given:
<figcaption>Text with <strong>formatting</strong></figcaption>
Returns:
{ "content": "Text with <strong>formatting</strong>" }

query source

Extracts arrays of objects from multiple elements:
images: {
	type: 'array',
	source: 'query',
	selector: 'img',
	query: {
		url: {
			type: 'string',
			source: 'attribute',
			attribute: 'src'
		},
		alt: {
			type: 'string',
			source: 'attribute',
			attribute: 'alt'
		}
	}
}
Given:
<div>
	<img src="large.jpg" alt="large image" />
	<img src="small.jpg" alt="small image" />
</div>
Returns:
{
	"images": [
		{ "url": "large.jpg", "alt": "large image" },
		{ "url": "small.jpg", "alt": "small image" }
	]
}

meta source (deprecated)

Meta attribute sources are deprecated. Use EntityProvider and related hooks instead.
Extracts values from post meta:
author: {
	type: 'string',
	source: 'meta',
	meta: 'author'
}
Requires server-side registration:
register_post_meta( 'post', 'author', array(
	'show_in_rest' => true,
	'single' => true,
	'type' => 'string'
) );

Selectors

The selector field uses CSS selector syntax:
// Tag selector
selector: 'img'

// Class selector
selector: '.book-author'

// ID selector
selector: '#main-title'

// Descendant selector
selector: '.card > h3'
Without a selector, the source runs against the block’s root element.

Default values

Provide fallback values with the default field:
// String default
{
	type: 'string',
	default: 'hello world'
}

// Array default
{
	type: 'array',
	default: [
		{ "url": "image1.jpg", "alt": "image" }
	]
}

// Object default
{
	type: 'object',
	default: {
		width: 100,
		title: 'title'
	}
}

Attribute roles

The role property provides semantic meaning:

content role

Marks user-editable content:
content: {
	type: 'string',
	source: 'html',
	selector: 'p',
	role: 'content'
}

local role

Marks temporary, non-persistent data:
blob: {
	type: 'string',
	role: 'local'
}
Local attributes are never saved to post content.

Using attributes

In the edit function

edit: function( { attributes } ) {
	return (
		<p>
			URL: { attributes.url }<br />
			Title: { attributes.title }<br />
			Size: { attributes.size }
		</p>
	);
}

In the save function

Attributes with a source must be saved correctly:
save: function( { attributes } ) {
	return (
		<div>
			<img src={ attributes.url } />
			<span>{ attributes.title }</span>
		</div>
	);
}
Attributes without a source are saved automatically in the comment delimiter.

Complete example

import { registerBlockType } from '@wordpress/blocks';
import { useBlockProps } from '@wordpress/block-editor';

registerBlockType( 'my-plugin/book', {
	attributes: {
		title: {
			type: 'string',
			source: 'html',
			selector: 'h3',
			role: 'content'
		},
		cover: {
			type: 'string',
			source: 'attribute',
			selector: 'img',
			attribute: 'src'
		},
		pages: {
			type: 'number',
			default: 0
		},
		size: {
			enum: [ 'small', 'medium', 'large' ],
			default: 'medium'
		}
	},
	edit: ( { attributes, setAttributes } ) => {
		const blockProps = useBlockProps();
		return (
			<div { ...blockProps }>
				<input
					value={ attributes.title }
					onChange={ ( e ) => setAttributes( { title: e.target.value } ) }
				/>
			</div>
		);
	},
	save: ( { attributes } ) => {
		const blockProps = useBlockProps.save();
		return (
			<div { ...blockProps }>
				<img src={ attributes.cover } />
				<h3>{ attributes.title }</h3>
			</div>
		);
	}
} );

Best practices

  • Store as much data as possible in HTML rather than comment delimiters
  • Use appropriate type values for validation
  • Provide default values for optional attributes
  • Use role: 'content' for user-editable content
  • Use descriptive attribute names
  • Consider deprecations when changing attributes

Build docs developers (and LLMs) love