Most of the time, you can’t create a dynamic webpage without having repeater fields. While developing the block you may want to give a feature to add repeatable content. In this tutorial, I am going to build a repeater block which having options to add a title, message, and image. These are the basic subfields you mostly require in the repeater. Of course, you can modify these fields as per the requirement.
Getting Started
I always preferred to build a dynamic block. This kind of block is easy to modify if your content structure is changed.
Open the terminal in your wp-content/plugins
directory and run the below command to create the plugin having a dynamic block.
npx @wordpress/create-block artisansweb-block --variant=dynamic
This command will create a plugin called artisansweb-block
. Activate this plugin.
Now to build a repeater, I’ll create 2 blocks. The multiple blocks can be created inside a single plugin. You don’t need to run the above command twice. I’ll show it in a moment.
The first block will have 3 fields(2 RichText and MediaUpload). Then inside the second(main) block, I’ll embed the first block using the concept of inner blocks. The inner blocks allow you to use existing blocks multiple times with additional controls like sorting, deleting, etc.
Once you’re finished with this tutorial, in the editor, you’ll be able to add the main block say Artisansweb – Repeater which will give the output as shown in the screenshot below. You can simply duplicate the child block as many times which acts exactly like a repeater.
Register Multiple Blocks Inside The Plugin
You already scaffold a plugin for the dynamic block. Under the src
directory, you’ll see a few files which are required to build a block. Create 2 directories inside this src
folder – block-title-message-image
and block-repeater
. Copy all files from the src
folder and paste them into these newly created directories.
The block-title-message-image
and block-repeater
are the dynamic blocks we are going to build in the next steps.
Register these blocks from the plugin’s main file as follows.
<?php
function create_block_artisansweb_block_block_init() {
register_block_type( __DIR__ . '/build' );
register_block_type( __DIR__ . '/build/block-title-message-image' );
register_block_type( __DIR__ . '/build/block-repeater' );
}
add_action( 'init', 'create_block_artisansweb_block_block_init' );
This way you can build as many blocks from the single plugin. While developing a WordPress website, it’s a better approach to develop all your blocks inside the main plugin.
Run the npm start
command from your plugin’s directory(plugins/artisansweb-block). This command compiles the code from the src
folder and generates the browser-supported code into the build
folder.
Modify The block.json
block-repeater/block.json
{
...
"name": "artisansweb/repeater",
"title": "Artisans Web - Repeater",
"description": "This Block allow you to insert repeatable content.",
...
}
Nothing fancy here. Everything is straightforward. The name value specified here will be used as a parent to the next block.
block-title-message-image/block.json
{
...
"name": "artisansweb/title-message-image",
"title": "Add Title, Message & Image",
"description": "This Block will be used in the Repeater Block.",
"attributes": {
"title": {
"type": "string"
},
"content": {
"type": "string"
},
"image": {
"type": "integer"
}
},
"parent": [
"artisansweb/repeater"
],
...
}
When we create a dynamic block, to store the content of a block attributes
are used. I have passed them as per the content we are going to save. Notice, the parent value as well. It is because this block will be used inside the inner blocks and available only within the parent block(artisansweb/repeater
).
Build The First Block
As we are taking 3 fields into the repeater, you are required to build this block which allows you to enter content – title, message, and image. It can be done using the RichText and MediaUpload components. The usage of these components I already explained in the linked article.
block-title-message-image/edit.js
import { useBlockProps, RichText, MediaUpload, MediaUploadCheck } from '@wordpress/block-editor';
import { Button, Spinner } from '@wordpress/components';
import { useSelect } from '@wordpress/data';
export default function Edit({ attributes, setAttributes }) {
const { mediaId, media } = useSelect( select => {
return {
mediaId: attributes.image,
media: select('core').getMedia(attributes.image)
}
}, [attributes.image] );
return (
<div { ...useBlockProps() }>
<RichText
tagName="p"
value={ attributes.title }
allowedFormats={ [] }
onChange={ ( title ) => setAttributes( { title } ) }
placeholder="Enter title here.."
/>
<RichText
tagName="p"
value={ attributes.content }
allowedFormats={ [] }
onChange={ ( content ) => setAttributes( { content } ) }
placeholder="Enter content here.."
/>
<MediaUploadCheck>
<MediaUpload
onSelect={ ( media ) =>
setAttributes( { image: media.id } )
}
allowedTypes={ ['image'] }
value={ attributes.image }
render={ ( { open } ) => (
<div>
{ ! mediaId && <Button variant="secondary" onClick={ open }>Upload Image</Button> }
{ !! mediaId && ! media && <Spinner /> }
{ !! media && media &&
<Button variant="link" onClick={ open }>
<img src={ media.source_url } width={200} />
</Button>
}
</div>
) }
/>
</MediaUploadCheck>
{ !! mediaId && media &&
<Button onClick={ () => setAttributes( { image: 0 } ) } isLink isDestructive>
Remove image
</Button>
}
</div>
);
}
Create Repeater Using Inner Blocks
The Inner Blocks are nested blocks where you can add more than one block under the one parent block. In the next step, I’ll build the inner block and inside it allow only the block we created in the previous step. The name of the block we created is artisansweb/title-message-image
as defined in block.json.
block-repeater/edit.js
import { useBlockProps, useInnerBlocksProps } from '@wordpress/block-editor';
import { BaseControl } from '@wordpress/components';
export default function Edit() {
const blockProps = useBlockProps();
const innerBlocksProps = useInnerBlocksProps(blockProps,
{
allowedBlocks: ['artisansweb/title-message-image'],
template: [
['artisansweb/title-message-image'],
['artisansweb/title-message-image']
]
},
);
return (
<BaseControl label={__("Artisans Web - Repeater")}>
<div {...innerBlocksProps} />
</BaseControl>
);
}
To save the content of inner blocks you need to pass <InnerBlocks.Content />
to the save() method of index.js as follows.
block-repeater/index.js
import { InnerBlocks } from '@wordpress/block-editor';
registerBlockType( metadata.name, {
edit: Edit,
save: () => <InnerBlocks.Content />,
} );
Now you can add the Artisansweb – Repeater block to the editor. You will get a predefined set of templates to enter the content. To add more content you can simply duplicate the child block(added inside the main block). Go ahead and add your repeatable content.
Display Repeater Content On Frontend
Usually, to print the content of dynamic blocks we used either the $content
or $attributes
variable. But in the case of inner blocks combined with the custom block, we need to use a different approach to render the frontend.
Inside our block-repeater/render.php
template, you have access to the $block
variable using which we can access the content of our block. The content is available to innerBlocks
property of the $block
variable. The below code will print content using the $block
variable.
<div <?php echo get_block_wrapper_attributes(); ?>>
<?php
if ( isset( $block->parsed_block['innerBlocks'] ) ) {
foreach ( $block->parsed_block['innerBlocks'] as $innerBlock ) {
if ( !empty( $innerBlock['attrs']['title'] ) ) {
printf("<h3>%s</h3>", $innerBlock['attrs']['title']);
}
if ( !empty( $innerBlock['attrs']['content'] ) ) {
printf("<p>%s</p>", $innerBlock['attrs']['content']);
}
if ( !empty( $innerBlock['attrs']['image'] ) ) {
printf("%s", wp_get_attachment_image($innerBlock['attrs']['image']), 'medium');
}
}
}
?>
</div>
Here, I loop through the innerBlocks
property and print the content stored as the attributes into the database.
This is how you can build a repeater in Gutenberg. For the sake of the tutorial, I used RichText and MediaUpload components. You are free to use any other components that fit to your needs.
If you liked this article, then please subscribe to our YouTube Channel for video tutorials.