Currently reading: Stories of Your Life and Others by Ted Chiang

List and edit your registered Gutenberg blocks


Depending on your situation, you may need a two-pronged approach to clean up the overly extensive list of blocks available in the Gutenberg inserter by default. Why would you want to do this? Well, for one thing you may not be using a "block theme" and thus have no use for any or most of the blocks acting as template partials.

Remove blocks with PHP

We first need to know what all the blocks are "called" as we'll be using their slug/name to remove them. On the wp_footer action (to get the output out of the way), query the block registry to get a list of all blocks, reduce the array to its keys, and dump the result. To also get their category, you'll need to loop through them instead.

add_action('wp_footer', function () {
    $all = WP_Block_Type_Registry::get_instance()->get_all_registered();

    // Just the names.
    var_dump(array_keys($all));

    // The name and category together.
    foreach ($all as $name => $block) {
        var_dump("{$block->category}: {$name}");
    }
});

We'll be using the allowed_block_types_all filter to pare down the list of blocks. Individual blocks can be targeted as can entire categories of them at once.

Note that this filter is kind of annoying: It sends you nothing but a true by default rather than a list of currently-registered blocks as would be nice. It therefore operates in a whitelist mode wherein you need to add every single block you want to use. All others will be ignored including new blocks added in core patches, by third-party plugins, or by your own hand. Thus, in whitelist mode you'll have to constantly edit it to include new blocks. Fun!

Let's try to make this filter act more like a blacklist instead. To do that we start with the list of all blocks, and from that we can clear out the unwanted ones by only keeping the ones we want. It's still a whitelist, but it's one made in a more programmatic manner versus extensive manual listing.

add_filter('allowed_block_types_all', function () {
    $everyone = WP_Block_Type_Registry::get_instance()->get_all_registered();

    // Definite keepers.
    $goodies = [
        'core/pattern',
        'core/shortcode',
        'core/html'
    ];

    // First: Kill off categories of blocks we don't want minus a few we need.
    foreach ($everyone as $name => $block) {
        if (in_array($name, $goodies)) continue;

        if (in_array($block->category, ['theme', 'widgets'])) {
            unset($everyone[$name]);
        }
    }

    // Definite crappers.
    $baddies = [
        'core/legacy-widget',
        'core/verse',
        'wpml/language-switcher'
    ];

    $goodies = [];

    // Then: Clean up the rest of the unwanteds in other or "null" categories.
    foreach (array_keys($everyone) as $block) {
        if (! in_array($block, $baddies)) {
            $goodies[] = $block;
        }
    }

    return $goodies;
});

On this basic site the code above drops the list of blocks from the left list (95 blocks) to the right list (40 blocks). Not bad.

array (
  0 => 'core/legacy-widget',
  1 => 'core/widget-group',
  2 => 'core/archives',
  3 => 'core/avatar',
  4 => 'core/block',
  5 => 'core/button',
  6 => 'core/calendar',
  7 => 'core/categories',
  8 => 'core/comment-author-name',
  9 => 'core/comment-content',
  10 => 'core/comment-date',
  11 => 'core/comment-edit-link',
  12 => 'core/comment-reply-link',
  13 => 'core/comment-template',
  14 => 'core/comments',
  15 => 'core/comments-pagination',
  16 => 'core/comments-pagination-next',
  17 => 'core/comments-pagination-numbers',
  18 => 'core/comments-pagination-previous',
  19 => 'core/comments-title',
  20 => 'core/cover',
  21 => 'core/file',
  22 => 'core/footnotes',
  23 => 'core/gallery',
  24 => 'core/heading',
  25 => 'core/home-link',
  26 => 'core/image',
  27 => 'core/latest-comments',
  28 => 'core/latest-posts',
  29 => 'core/list',
  30 => 'core/loginout',
  31 => 'core/media-text',
  32 => 'core/navigation',
  33 => 'core/navigation-link',
  34 => 'core/navigation-submenu',
  35 => 'core/page-list',
  36 => 'core/page-list-item',
  37 => 'core/pattern',
  38 => 'core/post-author',
  39 => 'core/post-author-biography',
  40 => 'core/post-author-name',
  41 => 'core/post-comments-form',
  42 => 'core/post-content',
  43 => 'core/post-date',
  44 => 'core/post-excerpt',
  45 => 'core/post-featured-image',
  46 => 'core/post-navigation-link',
  47 => 'core/post-template',
  48 => 'core/post-terms',
  49 => 'core/post-title',
  50 => 'core/query',
  51 => 'core/query-no-results',
  52 => 'core/query-pagination',
  53 => 'core/query-pagination-next',
  54 => 'core/query-pagination-numbers',
  55 => 'core/query-pagination-previous',
  56 => 'core/query-title',
  57 => 'core/read-more',
  58 => 'core/rss',
  59 => 'core/search',
  60 => 'core/shortcode',
  61 => 'core/site-logo',
  62 => 'core/site-tagline',
  63 => 'core/site-title',
  64 => 'core/social-link',
  65 => 'core/tag-cloud',
  66 => 'core/template-part',
  67 => 'core/term-description',
  68 => 'core/audio',
  69 => 'core/buttons',
  70 => 'core/code',
  71 => 'core/column',
  72 => 'core/columns',
  73 => 'core/details',
  74 => 'core/embed',
  75 => 'core/freeform',
  76 => 'core/group',
  77 => 'core/html',
  78 => 'core/list-item',
  79 => 'core/missing',
  80 => 'core/more',
  81 => 'core/nextpage',
  82 => 'core/paragraph',
  83 => 'core/preformatted',
  84 => 'core/pullquote',
  85 => 'core/quote',
  86 => 'core/separator',
  87 => 'core/social-links',
  88 => 'core/spacer',
  89 => 'core/table',
  90 => 'core/text-columns',
  91 => 'core/verse',
  92 => 'core/video',
  93 => 'prismatic/blocks',
  94 => 'core/post-comments',
);
array(
  0 => 'core/block',
  1 => 'core/button',
  2 => 'core/comment-template',
  3 => 'core/cover',
  4 => 'core/file',
  5 => 'core/footnotes',
  6 => 'core/gallery',
  7 => 'core/heading',
  8 => 'core/home-link',
  9 => 'core/image',
  10 => 'core/list',
  11 => 'core/media-text',
  12 => 'core/navigation-link',
  13 => 'core/navigation-submenu',
  14 => 'core/pattern',
  15 => 'core/shortcode',
  16 => 'core/audio',
  17 => 'core/buttons',
  18 => 'core/code',
  19 => 'core/column',
  20 => 'core/columns',
  21 => 'core/details',
  22 => 'core/embed',
  23 => 'core/freeform',
  24 => 'core/group',
  25 => 'core/html',
  26 => 'core/list-item',
  27 => 'core/missing',
  28 => 'core/more',
  29 => 'core/nextpage',
  30 => 'core/paragraph',
  31 => 'core/preformatted',
  32 => 'core/pullquote',
  33 => 'core/quote',
  34 => 'core/separator',
  35 => 'core/spacer',
  36 => 'core/table',
  37 => 'core/text-columns',
  38 => 'core/video',
  39 => 'prismatic/blocks',
);

Remove block variations with JS

That's all well and good, but what about blocks you want to keep that have variations you know you'll never use? The core/embed block is a great example because it's in the lists above only once but has a bunch of variations to support all manner of embedded content. Whomst will ever need all of these things cluttering up the list? Let's chop it down to two video hosts!

You'll have to enqueue your JS on a specific hook meant for assets attached to the editor. Doing it on admin_enqueue_scripts won't work because of reasons. We also need to make sure ours comes in after a couple requisite core libraries.

add_action('enqueue_block_editor_assets', function () {
    wp_enqueue_script('embed-chop', 'js/embed-chop.js', ['wp-blocks', 'wp-dom-ready']);
});

It's then a simple matter of grabbing the variations from the core/embed block and unregistering the ones that don't match the two we aim to keep.

wp.domReady(() => {
    const allowedEmbedBlocks = ['youtube', 'vimeo'];

    wp.blocks.getBlockType('core/embed').variations.forEach((variation) => {
        if (allowedEmbedBlocks.indexOf(variation.name) === -1) {
            wp.blocks.unregisterBlockVariation('core/embed', variation.name);
        }
    });
});

Why can't you deal with variations with PHP? Well, they're presumably not set up with PHP. Why that's the case is anyone's guess. More generally, why has the web development community gone crazy with JS over the past decade and turned basic text editing into a required build suite and local server using 1,400 dependencies across 42,000 files stuffed into a .node_modules directory? The march of progress…

Leave a comment

Your email address will not be published. Required fields are marked *