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:

repeater field in Gutenberg plugin sidebars
I used two simple text fields inside the repeater but you can actually use whatever you want. There is also a separate tutorial on my blog about creating custom panels.

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

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

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

Follow me on X