Repeater Field in Gutenberg Plugin Sidebars
I already have a plenty of tutorials on my blog where we created different kind of fields for Gutenberg sidebars, for example an image field, a gallery field, a checklist and even a dropdown with posts.
There is also my Simple Fields plugin for those of you guys who don’t want to mess up with JSX and React and prefer to use simple PHP code snippets instead.
But now let’s talk about creating a repeater field. First things first – a screenshot:

Registering Complex Array / Object Meta Fields in REST API with register_meta()
In a couple of my previous tutorials I already mentioned, that using register_meta()
in cases when metadata contains arrays could be a little tricky. But now it is going to contain not only an array of strings or numbers but an array of objects, so a kind of complex thing.
Let’s try to describe it schema
.
// I created a variable for that to make it more clear
$schema = array(
'type' => 'array',
'items' => array(
'type' => 'object',
'properties' => array(
'url' => array( 'type' => 'string' ),
'site_name' => array( 'type' => 'string' ),
),
)
);
register_meta(
'post',
'misha_links',
array(
'type' => 'array',
'single' => true,
'default' => array(),
'show_in_rest' => array( 'schema' => $schema )
)
);
You can also use register_post_meta()
function for this purpose. But in any case do not forget to wrap these functions inside rest_api_init
action hook.
Creating RepeaterControl Component
For your convenience you can also find the whole component on GitHub.
Before you check the code below, I would like to highlight the idea that the field depends on the structure of metadata in the database. For example for gallery we used an array of image IDs, but what are we using for a repeater field? An array of objects as you’ve probably noticed where each element of the array – is a repeater iteration but each object property – is a specific field in the repeater. And all that is in repeaterValues
variable below.
const { useSelect, useDispatch } = wp.data
const { TextControl, Button } = wp.components
const RepeaterControl = ( props ) => {
// get metadata
let repeaterValues = useSelect(
select => select('core/editor').getEditedPostAttribute('meta')?.[props.meta_key]
);
// function that updates metadata (we are going to use it a couple times)
const { editPost } = useDispatch( 'core/editor', [ repeaterValues ] );
// display the repeater
return <>
{ repeaterValues.map( (row, index) => {
return <div style={{marginBottom: '30px'}}>
<strong>Link {index + 1}</strong>
And here we are going to display fields
{ index > 0 && <Button isLink isDestructive onClick={ () => {
repeaterValues = repeaterValues.filter((obj,loopIndex) => loopIndex !== index)
editPost( { meta: { [props.meta_key]: repeaterValues } } )
}}>Remove line {index + 1}
</Button> }
</div>
} ) }
<Button
variant="secondary"
onClick={() => {
repeaterValues.push({})
repeaterValues = repeaterValues.splice(0)
editPost( { meta: { [props.meta_key]: repeaterValues } } )
} }>Add Item
</Button>
</>
}
export default RepeaterControl
- I can not enjoy enough the replacement of
withSelect
andwithDispatch
withuseSelect
anduseDispatch
hooks. It simplifies our component tremendously. - You can also see two
<Button>
components above, each of them is working with repeaterValues as well. - Please also check the tutorials – how to loop in JSX and how to create conditions.
And here is an example of a field code:
<TextControl
label="Set URL"
value={ repeaterValues[index]['url'] }
onChange={ (value) => {
// push our new value to repeaterValues
repeaterValues = repeaterValues.map((row, innerIndex) => {
return innerIndex === index ? {...row, ['url']: value} : row
});
editPost( { meta: { [props.meta_key]: repeaterValues } } )
} }
/>

Misha Rudrastyh
Hey guys and welcome to my website. For more than 10 years I've been doing my best to share with you some superb WordPress guides and tips for free.
Need some developer help? Contact me