ResponsiveControl
Summary
Section titled “Summary”Makes any control responsive: renders a label and a breakpoint switcher, then hands the resolved per-breakpoint value to a render prop.
When to use / When not to use
Section titled “When to use / When not to use”Use it whenever a block setting should differ per breakpoint. It is the shortest path from a
plain RangeControl to a responsive one.
Do not use it if you need the switcher somewhere other than beside the control — compose
useBreakpoint, useResponsiveAttribute and BreakpointSwitcher yourself instead.
Do not use it for values that are not stored on block attributes; the hooks are the lower level building block for post meta or custom stores.
Import
Section titled “Import”import { ResponsiveControl } from '@isudev/gutenberg/controls';| Name | Type | Default | Required | Description |
|---|---|---|---|---|
attrName |
string |
— | Yes | Base attribute name, e.g. 'columnGap'. |
attributes |
Record<string, unknown> |
— | Yes | The block’s attributes. |
setAttributes |
( next: Record<string, unknown> ) => void |
— | Yes | The block’s setAttributes. |
children |
( args: ResponsiveControlRenderArgs ) => ReactNode |
— | Yes | Renders the control with resolved values. |
label |
string |
undefined |
No | Visible label shown beside the switcher. |
variant |
'inline' | 'dropdown' |
'inline' |
No | Switcher layout. |
breakpoints |
Breakpoint[] |
DEFAULT_BREAKPOINTS |
No | The breakpoint set to offer. |
syncToEditor |
boolean |
false |
No | Push breakpoint changes to the editor’s device preview. |
syncFromEditor |
boolean |
false |
No | Follow the editor’s device preview. |
showReset |
boolean |
true |
No | Show a reset button when the active breakpoint has an override. |
className |
string |
undefined |
No | Extra class name on the root element. |
Examples
Section titled “Examples”A responsive range
Section titled “A responsive range”<ResponsiveControl attrName="columnGap" label={ __( 'Column Gap' ) } attributes={ attributes } setAttributes={ setAttributes }> { ( { value, inheritedValue, hasOwnValue, onChange } ) => ( /* * `RangeControl` has no `placeholder` — unknown props are spread onto its * `<input type="range">`, where one is inert. So the slider shows the value that * actually applies and `help` says where it came from. `??`, not `||`: an explicit * `0` is a real override. */ <RangeControl min={ 0 } max={ 100 } value={ value ?? inheritedValue } help={ hasOwnValue || inheritedValue === undefined ? undefined : `${ __( 'Inherited:' ) } ${ inheritedValue }` } onChange={ onChange } __next40pxDefaultSize __nextHasNoMarginBottom /> ) }</ResponsiveControl>Bind inheritedValue to a placeholder only on controls that have one — TextControl and
InputControl forward it to their <input>:
{ ( { value, inheritedValue, onChange } ) => ( <TextControl value={ value ?? '' } placeholder={ inheritedValue === undefined ? undefined : String( inheritedValue ) } onChange={ onChange } __next40pxDefaultSize __nextHasNoMarginBottom />) }Compact switcher, linked to the editor preview
Section titled “Compact switcher, linked to the editor preview”<ResponsiveControl attrName="layout" label={ __( 'Layout' ) } variant="dropdown" syncToEditor attributes={ attributes } setAttributes={ setAttributes }> { ( { value, onChange } ) => ( /* * The empty option matters: with no override, `value` is `undefined` and a native * `<select>` would otherwise show the first option as if it were set. */ <SelectControl value={ value ?? '' } options={ [ { label: '—', value: '' }, ...LAYOUT_OPTIONS ] } onChange={ onChange } __next40pxDefaultSize __nextHasNoMarginBottom /> ) }</ResponsiveControl>Rendering what the frontend would use
Section titled “Rendering what the frontend would use”{ ( { resolvedValue } ) => <p>{ `Applied: ${ resolvedValue }` }</p> }Behavior
Section titled “Behavior”- The base breakpoint’s value lives in
attrName; others live inattrName + suffix, socolumnGap,columnGapTablet,columnGapMobile. valueis the active breakpoint’s own value and isundefinedwhen it has none.inheritedValueis what it would fall back to — bind it toplaceholderon controls that have one, otherwise surface it throughhelp.resolvedValueis what actually applies.- Reset writes
undefined, so the attribute returns to itsblock.jsondefault and disappears from serialized markup. - The reset button never appears on the base breakpoint: its value is not an override.
- Editor sync is off unless you opt in, per direction.
Styling
Section titled “Styling”Ships no stylesheet. Layout uses Flex from @wordpress/components; pass className to
position the whole row.
Gotchas
Section titled “Gotchas”childrenis a function, not an element. Passing an element renders nothing useful.- Numeric controls:
0is a real value and will not fall back to an inherited value. This is deliberate. Merge with??if you must merge at all —value || inheritedValueshows the inherited value where0was set on purpose. RangeControlhas noplaceholderprop, so passinginheritedValueto one does nothing visible: it is spread onto the underlying<input type="range">and ignored. Reserve that pattern forTextControl/InputControland usevalue ?? inheritedValueplushelpelsewhere.- Attributes must be declared in
block.jsonfor every breakpoint you offer —columnGapTabletandcolumnGapMobiledo not spring into existence. syncFromEditorwithoutsyncToEditormakes the switcher read-only in practice. Clicking a breakpoint selects it, then the effect that follows the editor’s device preview sees an unchanged device type and reverts the selection — nothing pushed the click outwards for it to agree with. Pass both flags for an interactive switcher tied to the preview, or neither for one that stands alone.
Related
Section titled “Related”BreakpointSwitcher— the switcher alone.useResponsiveAttribute,useBreakpoint— the pieces underneath.- Decision 0003 — why base plus suffixes, and why there is no
defaultbreakpoint.