For WordPress developers, it’s essential to learn custom block development. I also started to learn it and decided to share my learning as I progressed with developing blocks. The custom blocks are built on top of components. Though WordPress provides a lot of components we first start with 2 components – RichText
and MediaUpload
.
Basically, in this tutorial, we are going to create a custom block to generate the following section for the website.
As you can see, there are 3 elements – heading, paragraph, and image are required to build this section. Here is the HTML markup for this section.
<div class="container">
<div class="left">
<h2>My Section Title</h2>
<p>Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.</p>
</div>
<div class="right">
<img src="images/1.jpg" width="600" />
</div>
</div>
And the CSS of this HTML is as follows.
.container {
display: flex;
}
.left, .right {
flex-grow: 1;
}
.left {
background-color: #f2f2f2;
padding: 20px;
}
.right {
background-color: #e6e6e6;
}
Take note markup and styling can differ in your case. My main focus is to show you how to build a WordPress block out of this HTML. So let’s begin with it.
If you want multiple images inside your block then check out my article – Upload Multiple Images Using MediaUpload Component.
Creating Dynamic Block
In WordPress, you can build 2 types of blocks – static and dynamic. The static block stores the HTML markup in the database. The dynamic block allows you to dynamically render markup using PHP on the front end. Using dynamic block, it just stores attributes to the database instead of a whole markup. I prefer to build a dynamic block as it gives full control to render the front end.
Having said that, let’s navigate to the wp-content/plugins
directory and run the below command to scaffold a dynamic block. You should have installed NodeJs on your system to run this command.
npx @wordpress/create-block artisansweb-block --variant=dynamic
It might take a couple of minutes to install this package. Upon completion, you’ll see a folder artisansweb-block
inside the plugins
directory. There are a couple of files to consider inside this block directory.
src/block.json
– In this file, you’ll define attributes to store the content of your block.src/edit.js
– This file is responsible for allowing you to add your content to the Gutenberg.src/render.php
– Generate the frontend markup using PHP.src/editor.scss
– As the extension suggests, you have to write styling using scss which gets applied on your block in the Gutenberg.src/style.scss
– The styling written to this file will be applied on the front end.
Go to your plugin’s page and activate this newly created plugin. Then, head over to this plugin directory inside the terminal and run the npm start
command. This command watches for the file changes inside the src
folder and compiles the final code using webpack inside the build
directory.
Adding Attributes to the block.json
For our section, we have to store 3 attributes in the database. I’ll name them as heading, message, and image. Inside your block.json
add these attributes as shown below.
{
...
"attributes": {
"heading": {
"type": "string"
},
"message": {
"type": "string"
},
"image": {
"type": "integer"
}
}
}
While passing attributes, you must pass the type value as per the content. Here, I passed the ‘string’ type for the heading and message. For the image attribute, I passed the ‘integer’ type. This is because I’m going to store the attachment id of the uploaded image.
RichText Component
To add the heading and paragraph, you can use the RichText component. This component allows you to format the content using HTML markup.
In order to use this component, you need to import it into your src/edit.js
file.
import { useBlockProps, RichText } from '@wordpress/block-editor';
After this, use the RichText
component to enter the heading and message as follows.
export default function Edit({ attributes, setAttributes }) {
return (
<div { ...useBlockProps() }>
<div className="container">
<div className="left">
<RichText
tagName="h2"
value={ attributes.heading }
allowedFormats={ [] }
onChange={ ( heading ) => setAttributes( { heading } ) }
placeholder="Enter heading here.."
/>
<RichText
tagName="p"
value={ attributes.message }
allowedFormats={ [] }
onChange={ ( message ) => setAttributes( { message } ) }
placeholder="Enter message here.."
/>
</div>
<div className="right">
{/* image upload here */}
</div>
</div>
</div>
);
}
To the Edit function, I’ve passed 2 arguments – attributes
and setAttributes
. They are used to set values for the attributes defined in the block.json
.
We can pass html elements as per our markup to the RichText component. I’ve used h2 and p tags respectively. With the onChange
event, the values you typed are set to the attributes.
Head over to the editor, search for Artisansweb Block, add it to the editor and you’ll see options to add your heading and paragraph.
MediaUpload Component
Along with the heading and paragraph, our section also requires the image. WordPress provides a MediaUpload component to open the media modal where you can insert a new image or select the existing image. You should import this component before use.
import { useBlockProps, RichText, MediaUpload, MediaUploadCheck } from '@wordpress/block-editor';
I am going to import a few more components to make the image upload work.
Button
: It adds a button. In our case, it’ll be the Upload Image button.Spinner
: It’s a loader that will appear until the image object loads.useSelect
: This hook is used to retrieve dynamic data from the server. As we’re storing the attachment id in the database, it needs to get an image object from the server using this attachment id.
import { Button, Spinner } from '@wordpress/components';
import { useSelect } from '@wordpress/data';
In the below code, I’m fetching the image object with the help of the useSelect
hook. Of course, it’ll be empty the first time.
export default function Edit({ attributes, setAttributes }) {
const { mediaId, media } = useSelect( select => {
return {
mediaId: attributes.image,
media: select('core').getMedia(attributes.image)
}
}, [attributes.image] )
....
}
Next, let’s add the MediaUpload
component in our custom block so the user selects the image which will appear in the editor. Optionally, users can also remove or replace the image.
<div className="right">
<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 } />
</Button>
}
</div>
) }
/>
</MediaUploadCheck>
{ !! mediaId && media &&
<Button onClick={ () => setAttributes( { image: 0 } ) } isLink isDestructive>
Remove image
</Button>
}
</div>
There are a couple of conditions used inside the render
props. You should be familiar with these conditions. With the help of these conditions, I added a Button, Spinner, and img tag.
! mediaId
– It checks whether the media is set or not.!! mediaId && ! media
– This means the media is set but not loaded yet. It takes a few moments to load as it’s coming from a server.!! media && media
– It indicates the media is set and its object is also available.!! mediaId
– The media is set.
On clicking the Remove image
button, I’m setting the image attribute’s value to 0 which means no media is selected.
Let’s add some styling to our custom block. Add the below code to the src/editor.scss
file.
.wp-block-create-block-artisansweb-block {
.container {
display: flex;
}
.left, .right {
flex-grow: 1;
}
.left {
background-color: #f2f2f2;
padding: 20px;
}
.right {
background-color: #e6e6e6;
}
}
I’ve used the parent class wp-block-create-block-artisansweb-block
in the above CSS. This auto-generated class is added to the parent div because of the following statement.
<div { ...useBlockProps() }>
Now you can try to add the content in this block. It should work now. Keep your Browser Console open to see the errors if any.
If you are curious about how WordPress stores your block data, go to your database and check out the post_content
column from the wp_posts
table. The data will be stored in something like the below format.
<!-- wp:create-block/artisansweb-block {"heading":"My Section Title","message":"Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.","image":28} /-->
Here artisansweb-block
is the name given inside the block.json
.
Render the Block on Front End
You’ve successfully completed the back-end part and you have content stored in your database. The next thing is to display this data on the front end. For this, open the src/render.php
file and use the code below for it.
<div <?php echo get_block_wrapper_attributes(); ?>>
<div class="wp-block-create-block-artisansweb-block_content">
<div class="left">
<h2><?php echo $attributes['heading']; ?></h2>
<p><?php echo $attributes['message']; ?></p>
</div>
<div class="right">
<?php echo wp_get_attachment_image($attributes['image'], 'medium'); ?>
</div>
</div>
</div>
Any attributes saved in the database are available to the $attributes
variable inside the render.php
file.
Add the styling for this section into the src/style.scss
file.
.wp-block-create-block-artisansweb-block_content {
display: flex;
.left, .right {
flex: 1;
}
.left {
background-color: #f2f2f2;
padding: 20px;
}
.right {
background-color: #e6e6e6;
position: relative;
}
.right img {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
}
Feel free to change styling as per your needs. I’m just adding a basic styling for the tutorial purpose.
That’s it! I hope you understand the basic workflow of custom block development in WordPress. I’ll keep posting new tutorials on block development regularly so stay tuned and let me know your thoughts in the comment section below.
If you liked this article, then please subscribe to our YouTube Channel for video tutorials.