Every block in the Block Editor must be contained within an HTML wrapper with specific attributes to function correctly. WordPress provides useBlockProps() to manage these wrapper attributes.
Proper wrapper attributes are essential when using custom styling or block supports.
Types of block markup
A block can have three distinct types of markup:
- Editor markup: Visual representation in the Block Editor (via
Edit component)
- Save markup: HTML saved to the database (via
save function)
- Dynamic render markup: Server-generated content (via
render_callback or render.php)
Editor markup
Use the useBlockProps() hook in your Edit component to define the wrapper:
import { useBlockProps } from '@wordpress/block-editor';
function Edit() {
const blockProps = useBlockProps();
return (
<p { ...blockProps }>
Hello World - Block Editor
</p>
);
}
What useBlockProps() provides
The useBlockProps() hook automatically adds:
- Unique
id attribute
- Accessibility attributes (
role, aria-label, tabindex)
- Data attributes for block identification
- The
wp-block class
- Block-specific class (e.g.,
wp-block-my-plugin-my-block)
- Classes and styles from block supports (color, spacing, etc.)
Generated editor markup
<p
tabindex="0"
id="block-4462939a-b918-44bb-9b7c-35a0db5ab8fe"
role="document"
aria-label="Block: My Block"
data-block="4462939a-b918-44bb-9b7c-35a0db5ab8fe"
data-type="my-plugin/my-block"
class="block-editor-block-list__block wp-block wp-block-my-plugin-my-block"
>
Hello World - Block Editor
</p>
Adding custom attributes
Pass additional classes and attributes to useBlockProps():
import { useBlockProps } from '@wordpress/block-editor';
function Edit( { attributes } ) {
const blockProps = useBlockProps( {
className: 'my-custom-class',
style: { color: attributes.textColor },
} );
return <div { ...blockProps }>Content</div>;
}
Block supports automatically add their classes and styles to the useBlockProps() return value.
Save markup
Use useBlockProps.save() in your save function to ensure proper wrapper attributes:
import { useBlockProps } from '@wordpress/block-editor';
function save() {
const blockProps = useBlockProps.save();
return (
<p { ...blockProps }>
Hello World - Frontend
</p>
);
}
What useBlockProps.save() provides
- Block-specific class name
- Classes and inline styles from block supports
- Any custom attributes passed as arguments
Generated save markup
<p class="wp-block-my-plugin-my-block">
Hello World - Frontend
</p>
With block supports enabled:
<p class="wp-block-my-plugin-my-block has-accent-color has-contrast-background-color has-text-color has-background">
Hello World - Frontend
</p>
Adding custom save attributes
import { useBlockProps } from '@wordpress/block-editor';
function save( { attributes } ) {
const blockProps = useBlockProps.save( {
className: 'custom-save-class',
} );
return <div { ...blockProps }>{ attributes.content }</div>;
}
Dynamic render markup (PHP)
For server-rendered blocks, use get_block_wrapper_attributes() in PHP:
function render_my_block( $attributes, $content, $block ) {
$wrapper_attributes = get_block_wrapper_attributes();
return sprintf(
'<div %1$s><p>%2$s</p></div>',
$wrapper_attributes,
esc_html( $attributes['content'] ?? '' )
);
}
Adding custom PHP attributes
Pass an array of custom attributes:
$wrapper_attributes = get_block_wrapper_attributes(
array(
'class' => 'my-custom-class',
'data-custom' => 'value',
)
);
get_block_wrapper_attributes() automatically includes classes from block supports like color, spacing, and typography.
Wrapper requirements
For both Edit and save, the wrapper must be:
✅ A standard DOM element (e.g., <div>, <p>, <section>)
✅ A React component that forwards all props to native DOM elements
❌ Not a React Fragment (<Fragment>)
❌ Not <ServerSideRender> component
Valid wrappers
// Standard DOM element
<div { ...useBlockProps() }>Content</div>
// React component that forwards props
import { Button } from '@wordpress/components';
<Button { ...useBlockProps() }>Click me</Button>
Invalid wrappers
// Fragment (will not work)
<>
<div { ...useBlockProps() }>Content</div>
</>
// ServerSideRender (will not work)
<ServerSideRender { ...useBlockProps() } />
Complete example
Edit component
import { useBlockProps, RichText } from '@wordpress/block-editor';
export default function Edit( { attributes, setAttributes } ) {
const blockProps = useBlockProps( {
className: 'my-editor-class',
} );
return (
<RichText
{ ...blockProps }
tagName="p"
value={ attributes.content }
onChange={ ( content ) => setAttributes( { content } ) }
/>
);
}
Save function
import { useBlockProps, RichText } from '@wordpress/block-editor';
export default function save( { attributes } ) {
const blockProps = useBlockProps.save();
return (
<p { ...blockProps }>
<RichText.Content value={ attributes.content } />
</p>
);
}
Render callback (PHP)
function render_my_block( $attributes ) {
$wrapper_attributes = get_block_wrapper_attributes(
array( 'class' => 'my-custom-class' )
);
ob_start();
?>
<div <?php echo $wrapper_attributes; ?>>
<p><?php echo esc_html( $attributes['content'] ?? '' ); ?></p>
</div>
<?php
return ob_get_clean();
}
Block supports integration
When you enable block supports in block.json, the wrapper automatically receives the generated classes:
"supports": {
"color": {
"text": true,
"background": true
},
"spacing": {
"padding": true,
"margin": true
}
}
Resulting markup:
<div class="wp-block-my-plugin-my-block has-text-color has-background has-padding has-margin">
Content
</div>
Next steps
Block supports
Learn about available block supports
Rendering
Understand static vs dynamic rendering