Disclaimer: The document is based on the proposed API by @ellatrix
and @gziolo
. See #48809.
Introduce a new API, registerBlockSupport, for describing the behavior and rendering of block features that utilize support
properties.
As the block editor started absorbing the complexities of block features like color, fonts, and dimension controls, it became apparent that the current method of integrating UI controls is not very sustainable and may sometimes hinder performance.
The present approach to add the "block support" features requires fragmenting the logic across multiple filters. Each filter is applied separately, overriding whole components multiple times. Additionally, it requires specific checks to ensure controls are only rendered when necessary and workarounds to prevent redundant re-mounting of the block's "Edit" component.
Alignment support
function addAttribute( settings ) {
if ( 'type' in ( settings.attributes?.align ?? {} ) ) {
return settings;
}
if ( hasBlockSupport( settings, 'align' ) ) {
// Gracefully handle if settings.attributes is undefined.
settings.attributes = {
...settings.attributes,
align: {
type: 'string',
// Allow for '' since it is used by updateAlignment function
// in withToolbarControls for special cases with defined default values.
enum: [ ...ALL_ALIGNMENTS, '' ],
},
};
}
return settings;
}
export const withToolbarControls = createHigherOrderComponent(
( BlockEdit ) => ( props ) => {
const blockEdit = <BlockEdit key="edit" { ...props } />;
return (
<>
<BlockControls group="block" __experimentalShareWithChildBlocks>
<BlockAlignmentControl
value={ props.attributes.align }
onChange={ ( nextAlign ) =>
props.setAttributes( { align: nextAlign } )
}
/>
</BlockControls>
{ blockEdit }
</>
);
},
'withToolbarControls'
);
export const withDataAlign = createHigherOrderComponent(
( BlockListBlock ) => ( props ) => {
const { attributes } = props;
const { align } = attributes;
// If an alignment is not assigned, there's no need to go through the
// effort to validate or assign its value.
if ( align === undefined ) {
return <BlockListBlock { ...props } />;
}
let wrapperProps = props.wrapperProps;
wrapperProps = { ...wrapperProps, 'data-align': align };
return <BlockListBlock { ...props } wrapperProps={ wrapperProps } />;
},
'withDataAlign'
);
export function addAssignedAlign( props, blockType, attributes ) {
const { align } = attributes;
props.className = classnames( `align${ align }`, props.className );
return props;
}
addFilter(
'blocks.registerBlockType',
'core/align/addAttribute',
addAttribute
);
addFilter(
'editor.BlockListBlock',
'core/editor/align/with-data-align',
withDataAlign
);
addFilter(
'editor.BlockEdit',
'core/editor/align/with-toolbar-controls',
withToolbarControls
);
addFilter(
'blocks.getSaveContent.extraProps',
'core/align/addAssignedAlign',
addAssignedAlign
);
The new first-class API will shift the implementation burden to the Block Editor, reducing the complexity for developers.
registerBlockSupport( 'align', {
blockSettings: {
attributes: {
align: { type: 'string' },
},
},
Controls( props ) {
return (
<BlockControls group="block" __experimentalShareWithChildBlocks>
<BlockAlignmentControl
value={ props.attributes.align }
onChange={ ( nextAlign ) =>
props.setAttributes( { align: nextAlign } )
}
/>
</BlockControls>
);
},
useBlockProps( attributes ) {
const { align } = attributes;
return { 'data-align': align };
},
getSaveProps( attributes ) {
const { align } = attributes;
return {
className: `align${ align }`,
};
},
} );
A support key based registrar; doesn't require a separate isSupported
check. The new settings object is merged into the block type definition.
The API is easy to use but lacks flexibility. e.g., Some block support features must consider multiple support keys before rendering UI.
registerBlockSupport( 'core/align', {
isSupported( blockType ) {
return hasBlockSupport( blockType, 'align' );
},
blockSettings( settings ) {
if ( 'type' in ( settings.attributes?.align ?? {} ) ) {
return settings;
}
settings.attributes = {
...settings.attributes,
align: {
type: 'string',
enum: [ ...ALL_ALIGNMENTS, '' ],
},
};
return settings;
},
EditControls( props ) {
return (
<BlockControls group="block" __experimentalShareWithChildBlocks>
<BlockAlignmentControl
value={ props.attributes.align }
onChange={ ( nextAlign ) =>
props.setAttributes( { align: nextAlign } )
}
/>
</BlockControls>
);
},
useBlockProps( { attributes, blockType } ) {
const { align } = attributes;
return { 'data-align': align };
},
getSaveProps( { attributes, blockType } ) {
const { align } = attributes;
return {
className: `align${ align }`,
};
},
} );
This results from "migrating" current block support implementations to the proposed API. It builds on the initial design but uses functions to determine block support and update block settings, as they seem more flexible.
In the future, we could potentially offer "shortcut" options for both isSupported
and blockSettings
.
- Should new API support adding inline styles and SVGs to the editor? See #48884
- Some features change the block transformation logic. Are we considering supporting it on the API level?