Building a custom Gutenberg block

It was easier than I thought. Follow me on my short journey to add a custom block to the WordPress editor.

Before restarting this blog I played a bit around with Ghost, and it has one very appealing feature – bookmark cards. These cards are a very nice way to link to other resources and expose the most relevant links in the article to the reader. Sadly, WordPress doesn’t offer them out of the box. There is a plugin that offers this functionality, but didn’t work reliably on my blog, and it also doesn’t handle YouTube links at all. You may ask why I want to use these cards for YouTube too. Another goal of this blog is to be cookie free, which is even impossible with the no cookie YouTube Links.

So, there is a demand and no proper solution. Guess what happened? I built something.

Possible Solutions

If you want to extend Gutenberg with a rich block, that does a bit more than just rendering some HTML, you basically have two options – build it using the official toolchain based on React or use some kind of plugin which allows you to build the block in PHP. I love React, but I don’t enjoy the various options for toolchains in this ecosystem and I also thought, that a simple problem should be solved in a simple way. I started to look into the various options for plugins and settled on two of them for the short list – Carbon Fields and MB Blocks. The first one is available for free and the other is a commercial plugin with an affordable price and excellent documentation.

About – Carbon Fields
History and background Carbon Fields was started as htmlBurger’s internal custom fields library in 2009. Back then, we were just starting our business and the state of the WordPress’s plugin ecosystem was not as developed as it is nowadays. So we had to build our own solutions for basic stuff like custom fields and theme…
About - Carbon Fields
MB Blocks – Creating Custom Gutenberg Blocks With PHP – Meta Box
Creating custom Gutenberg blocks with PHP. No React, Webpack or Babel. Beautiful syntax, powerful features.
MB Blocks - Creating Custom Gutenberg Blocks With PHP - Meta Box

Of course, I tried Carbon Fields first, but even though it seems to be a perfect solution to add custom fields to your blog, the functionality covering Gutenberg blocks is a bit unstable from my point of view. I wasn’t able to create a single block that didn’t break my blog. But I have to be fair too. I am a developer for a very long time, but I have no in-depth knowledge about PHP. A skilled PHP developer could have solved the problems easily.

Meta Box Blocks worked immediately, and I plan to create some more blocks in the future. So, I spend the $59 on this plugin and activated it.

Creating the block

I recommend using the Code Snippets plugin to inject the block code into your WordPress installation. It offers a code editing environment right in your admin area and also allows you to define when various code snippets should be executed. It is a safer solution then editing the functions.php file of your theme which might be overwritten by an update. Meta Box also offers a visual editor for these blocks, but this tools costs an extra $49 which I didn’t want to invest. And I am already using Code Snippets to inject various small PHP snippets to tweak the blog here and there.

Code Snippets
An easy, clean and simple way to run code snippets on your site.
Code Snippets

The Code

The code of this snippet is pretty easy:

  • Define the metadata fields for the editor.
  • Add a function to retrieve the Open Graph data of the site we want to link to.
  • Define the render callback that creates the final output.

Way easier than to set up a JavaScript-based project.

<?php add_filter('rwmb_meta_boxes', function ($meta_boxes) { $meta_boxes[] = [ 'title' => 'Bookmark Card', 'id' => 'bookmark-card', 'icon' => 'fas fa-link', 'type' => 'block', 'category' => 'common', 'fields' => [ [ 'type' => 'url', 'id' => 'url', 'name' => 'URL', 'required' => true, 'size' => "100", ], [ 'type' => 'select', 'id' => 'mode', 'name' => 'Mode', 'options' => array( 'horizontal' => 'Horizontal (Small card)', 'vertical' => 'Vertical (Large card)' ) ], ], 'render_callback' => 'render_bookmark_card_callback', 'enqueue_style' => get_template_directory_uri() . '/blocks/bookmark-card/style.css' ]; return $meta_boxes; }); function fetch_og($url) { $data = file_get_contents($url); $dom = new DomDocument; @$dom->loadHTML($data); $xpath = new DOMXPath($dom); $metas = $xpath->query('//*/meta[starts-with(@property, \'og:\')]'); $og = array(); foreach ($metas as $meta) { $property = str_replace('og:', '', $meta->getAttribute('property')); $content = $meta->getAttribute('content'); $og[$property] = $content; } $icons = $xpath->query('//*/link[starts-with(@rel, \'shortcut icon\')]'); if ($icons->length > 0) { $og['favicon'] = $icons[0]->getAttribute("href"); } else { $og['favicon'] = ""; } return $og; } function render_bookmark_card_callback($attributes, $is_preview = false, $post_id = null) { $url = mb_get_block_field('url'); $og = fetch_og($url); ?> <figure class="wp-block-oliverandrich-bookmark-card"> <a class="bookmark-card is-<?= mb_get_block_field('mode') ?>" href="<?= $url ?>"> <div class="bookmark-card__image"> <img src="<?= $og['image'] ?>"> </div> <div class="bookmark-card__content"> <div class="bookmark-card__title"><?= $og['title'] ?></div> <div class="bookmark-card__description"><?= $og['description'] ?></div> <div class="bookmark-card__meta"> <img class="bookmark_card__meta-icon" src="<?= $og['favicon'] ?>"> <span class="bookmark_card__meta-publisher"><?= parse_url($url, PHP_URL_HOST) ?></span> </div> </div> </a> </figure> <?php } ?>
Code language: PHP (php)

I also had to create some CSS for these boxes too.

.wp-block-oliverandrich-bookmark-card { display: flex; border: 1px solid rgba(0, 0, 4,.075); border-radius: 8px; box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05); overflow: hidden; margin-bottom: 25px; } a.bookmark-card { display: flex; flex-direction: column; color: inherit; font-size: 16px; line-height: 1.3; text-decoration: none !important; } { flex-direction: row-reverse; } .bookmark-card__content { padding: 16px; border-style: solid; border-color: rgba(0, 0, 4,.075); border-width: 1px 0 0 0; } .is-horizontal .bookmark-card__content { display: flex; flex-direction: column; flex: 3 1 160px; justify-content: space-between; border-width: 0 1px 0 0; } .bookmark-card__image img { display: block; object-fit: cover; width: 100%; height: 100%; } .is-horizontal .bookmark-card__image { flex: 1 1 160px; } .bookmark-card__title { font-weight: 600; } .is-horizontal .bookmark-card__title { display: -webkit-box; -webkit-line-clamp: 1; -webkit-box-orient: vertical; overflow: hidden; } .bookmark-card__description { display: -webkit-box; -webkit-line-clamp: 4; -webkit-box-orient: vertical; overflow: hidden; } .is_horizontal .bookmark-card__description { -webkit-line-clamp: 2; } .bookmark-card__title, .bookmark-card__description { margin-bottom: 8px; } .bookmark-card__meta { display: flex; flex-direction: row; gap: 4px; align-items: center; } .bookmark_card__meta-icon { width: 16px; height: 16px; } .bookmark_card__meta-icon[src=""] { width: 0; height: 0; display: none; } .bookmark_card__meta-publisher { display: -webkit-box; -webkit-line-clamp: 1; -webkit-box-orient: vertical; overflow: hidden; }
Code language: CSS (css)

Would I do it again?

Yes, based on the experience today I will definitely build more blocks using the Meta Box Blocks plugin. But before that, I have to fix some bugs that I discovered while writing this blogpost.

And here is a YouTube video!

As I said in the beginning. I wanted to link to and “embed” YouTube videos in a GDPR-compliant and cookie free way. Here is a video about the project that is currently most fascination me – Operation Night Watch.

Operation Night Watch
“Now we are getting information that we always wanted but could never get before.”What is the exact state of the Night Watch (1642)? To find out, Operation N…
Operation Night Watch