In Part 3 we hardcoded calloutType: 'info'. In Part 5 we let users change it through the inspector sidebar.
The InspectorControls slot
InspectorControls is a slot component. Whatever you render inside it appears in the block’s right-sidebar inspector — not inline in the block.
import { InspectorControls, useBlockProps, RichText } from '@wordpress/block-editor';
import { PanelBody, SelectControl, ToggleControl } from '@wordpress/components';
import { __ } from '@wordpress/i18n';
export default function Edit({ attributes, setAttributes }) {
const { title, body, calloutType, dismissible } = attributes;
const blockProps = useBlockProps({
className: `my-callout my-callout-${calloutType}`,
});
return (
<>
<InspectorControls>
<PanelBody title={__('Callout settings', 'my-first-block')} initialOpen>
<SelectControl
label={__('Type', 'my-first-block')}
value={calloutType}
options={[
{ label: 'Info', value: 'info' },
{ label: 'Tip', value: 'tip' },
{ label: 'Warning', value: 'warning' },
{ label: 'Error', value: 'error' },
]}
onChange={(value) => setAttributes({ calloutType: value })}
/>
<ToggleControl
label={__('Dismissible', 'my-first-block')}
help={__('Show a close button.', 'my-first-block')}
checked={dismissible}
onChange={(value) => setAttributes({ dismissible: value })}
/>
</PanelBody>
</InspectorControls>
<div {...blockProps}>
<RichText
tagName="h4"
value={title}
onChange={(value) => setAttributes({ title: value })}
/>
<RichText
tagName="p"
value={body}
onChange={(value) => setAttributes({ body: value })}
/>
</div>
</>
);
}
Add dismissible to block.json attributes:
"dismissible": { "type": "boolean", "default": false }
Common controls from @wordpress/components
| Component | Use |
|---|---|
SelectControl | Dropdown for fixed-choice options |
ToggleControl | Boolean on/off |
TextControl | Single-line text |
TextareaControl | Multi-line text |
ColorPalette | Theme-aware color picker |
FontSizePicker | Theme-aware font size |
RangeControl | Numeric slider |
ButtonGroup | Multi-button selection |
PanelBody | Collapsible section wrapper |
RadioControl | Radio button group |
Group related controls
Wrap related controls in a PanelBody. Each panel is collapsible. For our Callout block:
<InspectorControls>
<PanelBody title="Appearance" initialOpen>
<SelectControl ... /> {/* type */}
</PanelBody>
<PanelBody title="Behavior" initialOpen={false}>
<ToggleControl ... /> {/* dismissible */}
</PanelBody>
</InspectorControls>
Toolbar controls (for block-toolbar, not the inspector)
If a control belongs in the toolbar above the selected block (alignment, link, bold), use BlockControls instead of InspectorControls:
import { BlockControls, AlignmentToolbar } from '@wordpress/block-editor';
<BlockControls>
<AlignmentToolbar
value={attributes.alignment}
onChange={(value) => setAttributes({ alignment: value })}
/>
</BlockControls>
Verification
Callout settings test
My Callout block
Heads up
Selecting “Warning” in the inspector changes the color theme to amber.
Insert your block. The right sidebar now shows “Callout settings” panel with a Type dropdown and Dismissible toggle. Change Type → the block re-renders with the new color theme immediately.
What’s next
Part 6 introduces nested blocks via InnerBlocks — letting users compose other blocks inside our Callout (so they can put a list or an image inside, not just text).
Get the next one in your inbox. Practical tips, no fluff.
More tutorials
View all-
Block Patterns
Register reusable block compositions — landing-page hero, feature grid, FAQ section — that users insert as a complete unit.
25 min
-
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.
30 min
-
Building a Block Plugin
Package your block as a proper WordPress plugin — file structure, asset enqueueing, server-side render callbacks, plugin metadata.
35 min