So you’re a WordPress developer and you get this awesome website design in Figma (or Zeplin or whatever works for you) and you need to make no less awesome WordPress website. How to achieve this?
If you have a pretty simple design with a simple grid — you can use Elementor. It’s easy to edit for users, has plenty of options so it won’t be hard for you to reproduce simple designs there.
But what if you have a complex design with uncommon elements like custom sliders, calculators, scrollytelling blocks? “I can do all this stuff with Elementor too”, — some say. And they will be kinda right. But can you imagine how tricky it will be with Elementor? To override all its styles, scripts and layouts? I’ve tried that, and that was a nightmare! You will get a total mess with CSS and HTML, so it will be very hard to maintain such a site. This is not what Elementor is for! Besides, you need to get through all the developer docs about how to make a custom widget in Elementor and dig into the code each time you want to add/remove some field or change its type or whatever else.
Kinda hard to build such a block in Elementor
Let me show how it can be achieved with native WordPress Gutenberg editor and Advanced Custom Fields Pro plugin!
Wait, what? The PRO version? Should I buy it?
It’s worth it! It provides such amazing features, I cannot imagine myself developing a WordPress site without that plugin now! Besides, you can download the PRO version from GitHub (uploaded by plugin’s owner Elliot Condon, so it’s legal 😎). Of course, it’s not the latest version, but it’s a good demo so you can take a closer look at this plugin.
So what’s this plugin for? As you may guess from its name — it adds custom fields of any type to any post type. That means you can add a text input, number input, image, video, embed object, color picker, datetime picker and many more. Basically, all that HTML can provide (correct me if I’m wrong in the comments 😁). Plus, you can create repeater fields and provide conditional logic to each field. Really, it’s a very powerful and useful plugin for WordPress developers.
This is only part of the field types ACF provides
And here’re some more field types
But let’s get back to the custom blocks. You can add those too! And manipulate custom fields from the WordPress admin panel. Your users will get a user-friendly back-end for the custom blocks, so they can easily set the data they need. And what’s more important, they won’t mess things up, because they will be allowed to change only the data you provide them to change. So it will be very hard to break anything on the page.
Register a new block
Let’s get to business! I’m gonna show you a bit of an old-school way of adding new custom blocks. But don’t worry, it has only slight changes compared to the modern way. Basically, the main difference is where to define parameters of a new block. So first, create a new subfolder in your theme’s folder to place your block’s files there. Something like this:
A folder tree of the new block
Then we need to register a new block and provide settings for it. I do that in functions.php
file using acf_register_block_type
function like this:
if( function_exists('acf_register_block_type') ) {
add_action('acf/init', 'custom_prefix_register_acf_blocks');
function custom_prefix_register_acf_blocks() {
acf_register_block_type(array(
'name' => 'my-block', // a system name of the block
'title' => __('My Block'),
'description' => __('Just another awesome block.'),
'render_template' => 'blocks/my-block/block.php',
'category' => 'common', // The core provided categories are [common | formatting | layout | widgets | embed].
'icon' => 'book-alt', // These can be any of WordPress’ Dashicons, or a custom svg element.
'keywords' => array( 'block', 'custom' ),
'supports' => array( 'anchor' => true )
));
}
}
Don’t forget to change custom_prefix_
to your theme’s unique prefix. I wrote a bit more about why you should use prefixes here:
DO NOT do this as a WordPress developer
Since ACF version 6.0 and WordPress 5.8+ it is recommended to register new blocks with a new block.json syntax, which requires you to create a file named block.json
(exactly that name) in the folder where your new block will be located (blocks/my-block
in our example). Then add your block’s parameters to that file like this:
{
"name": "acf/my-block",
"title": "My Block",
"description": "Just another awesome block.",
"style": ["file:./block.css"],
"category": "common",
"icon": "book-alt",
"keywords": ["block", "custom"],
"acf": {
"mode": "preview",
"renderTemplate": "block.php"
},
"align": "full"
}
Note, that we need to provide a namespace called acf to the block’s name parameter (“name”: “ **acf/** my-block”
). Then you need to register your block in your theme’s functions.php file using a register_block_type
function (not acf_register_block_type
) like this:
if( function_exists('acf_register_block_type') ) {
add_action( 'init', 'custom_prefix_register_acf_blocks' );
function custom_prefix_register_acf_blocks() {
register_block_type( __DIR__. '/blocks/my-block' );
}
}
But if you’re using a GitHub version of ACF Pro plugin (which is 5.9.1 version) you should use the legacy way of registering custom blocks.
Add a new block
To add a new custom Gutenberg block follow this steps:
1.In your wp-admin
go to Custom Fields -> Add new:
- Provide a new field group title, select the group’s location to Block and choose your block’s name from the list (
My Block
in our example).
- Add fields to your block (+ Add Field button). For example, if you want your user to have the ability to change some text in your block — add a text field.
- Add as many fields as you need. If you new with ACF plugin — take a look of detailed documentation about each of field types here: https://www.advancedcustomfields.com/resources/#field-types
As soon as you’re done — click on the Publish button.
Create a template file for your block. Add a new file in your block’s folder (
blocks/my-block
) that matches therender_template
setting used when registering the block (block.php
in this example).Add a PHP and HTML code of your block in that file. For example:
<?php
/**
* Custom Block Template.
*
* @param array $block The block settings and attributes.
* @param string $content The block inner HTML (empty).
* @param bool $is_preview True during backend preview render.
* @param int $post_id The post ID the block is rendering content against.
* This is either the post ID currently being displayed inside a query loop,
* or the post ID of the post hosting this block.
* @param array $context The context provided to the block by the post or it's parent block.
*/
// Support custom "anchor" values.
$anchor = '';
if ( ! empty( $block['anchor'] ) ) {
$anchor = 'id="' . esc_attr( $block['anchor'] ) . '" ';
}
// Create a class attribute allowing for custom "className" and "align" values.
$class_name = 'custom-block';
if ( ! empty( $block['className'] ) ) {
$class_name .= ' ' . $block['className'];
}
if ( ! empty( $block['align'] ) ) {
$class_name .= ' align' . $block['align'];
}
// Load values and assign defaults.
$text_to_change = get_field( 'text_to_change' ) ?: __('Default text', 'theme-slug');
$image_to_change = get_field( 'image_to_change' ) ?: 112; // returns image ID
?>
<div <?php echo $anchor; ?>class="<?php echo esc_attr( $class_name ); ?>">
<div class="custom-block">
<h3 class="custom-block__text">
<?php echo esc_html( $text_to_change ); ?>
</h3>
<div class="custom-block__image">
<?php echo wp_get_attachment_image( $image_to_change['ID'], 'full' ); ?>
</div>
</div>
</div>
The only function here that comes from the ACF plugin is get_field
. It returns a value of the field you added in the field group called My Block (step 2).
- Don’t forget to add CSS styles to your block. You can do that either in your theme’s stylesheet file or in the file
block.css
if you use a newblock.json
syntax (that file goes to the folder with yourblock.php
file). To apply custom block styles to the Gutenberg editor use this code:
function custom_prefix_theme_setup() {
// Add support for editor styles.
add_theme_support( 'editor-styles' );
// Enqueue editor styles.
add_editor_style( 'blocks/my-block/block.css' ); // relative path from your theme's directory
}
add_action( 'admin_init', 'custom_prefix_theme_setup' );
- All we have to do now is to add our new custom block in the WordPress page or post:
An example of a custom Gutenberg block made with ACF Pro plugin
I’ve made a very simple block just to demonstrate how easy it can be done, you can learn more about custom blocks from the ACF docs: https://www.advancedcustomfields.com/resources/blocks/
Why do I need to use custom Gutenberg blocks again?
As you may have noticed, the HTML markup I use in the example is completely arbitrary. No need to deal with extra elements like in Elementor or other WordPress builders. As well as CSS — your block only inherits basic styles from your site (like from <body>
or <html>
tags). And it’s super easy for users to change any data you provide them to change. So you can create a very complex custom blocks with lots of settings and your users can easily maintain them from the admin without disturbing you each time they want “to change that button text” 😄.
What do you think about this bundle Gutenberg + ACF? Don’t hesitate to write your thoughts in the comments below.
If you find this article helpful — don’t hesitate to like, subscribe and leave your thoughts in the comments 😊
Read more posts on my Medium blog
Thanks for reading!
Stay safe and peace be with you!
Top comments (2)
Hi, thanks for this article. A couple of things that didn't work for me.
$image_to_change = get_field( '$image_to_change' )
need to be
$image_to_change = get_field( 'image_to_change' )
<?php echo wp_get_attachment_image( $image_to_change, 'full' ); ?>
need to be
<?php echo wp_get_attachment_image( $image_to_change['ID'], 'full' ); ?>
register_block_type( __DIR__. '/blocks/my-block );
need to be
register_block_type( __DIR__. '/blocks/my-block' );
:)
A.
Hi Allesandro!
Good eye! Thanks a lot for notice those typos, I will fix those :)
BTW, here <?php echo wp_get_attachment_image( $image_to_change, 'full' ); ?> it depends on which type your custom field returns. By default it's array, but if you select ID - then you will get an ID of the image (int), so no need to extract it from array like this $image_to_change['ID'].