WordPress.org

WordPress Developer Blog

One hook to rule them all: The many faces of block categories

One hook to rule them all: The many faces of block categories

You’ve probably noticed that WordPress blocks come organized in categories. But did you know you don’t have to live with those categories? You can change them around, to make them work for you.

You can rename them. Reorder them. Add completely new categories, including new categories for your custom blocks. And probably do things you or I haven’t even thought of yet.

And you can do all of that with one hook: block_categories_all

The hook is a versatile tool that allows developers to customize the way block categories are organized. Throughout this article you will learn how to use this hook and build functions to add, reorder and rename single or multiple block categories.

Before you dive in it’s important to note that each function accepts a single parameter which is expected to be an array of existing block categories. This is defined as $categories throughout this article.

Adding categories

If you build custom blocks, you might want to put them in some custom categories. You could also have other uses for custom categories—maybe you’re building some sections that have custom styling. Whatever your reasons, here are three approaches:

Creating a new category

Here’s how you can use array_merge to add a new category named Custom Blocks to the end of the existing categories array.

add_filter( 'block_categories_all', 'add_block_category', 10, 2 );

function add_block_category( $categories ) {
    $custom_category = array(
        array(
            'slug'  => 'custom-blocks',
            'title' => __( 'Custom Blocks', 'your-text-domain' ),
            'icon'  => null,
        ),
    );

    return array_merge( $categories, $custom_category );
}

Start by creating a function that accepts a single parameter—remember, it’s expecting the list of existing $categories. Inside the function create a variable named $custom_category and assign an array with these key-value pairs:

  • slug — a unique identifier for the category
  • title — the display name of the category
  • icon — a Dashicon or custom SVG

Note: to my knowledge we’re not using or showing the icon anywhere so in this example you can set this as null.

Then you’ll use the array_merge function to combine $custom_category with the existing $category list. You can position your new category before or after $categories based on the order that you pass each variable into the array_merge.

Positioning a new category

Here’s how to use array_splice to add a new category named Custom Blocks to $categories in the second position (index 1).

add_filter( 'block_categories_all', 'add_order_block_category', 10, 2 );

function add_order_block_category( $categories ) {
    $custom_category = array(
        'slug'     => 'custom-blocks',
        'title'    => __( 'Custom Blocks', 'your-text-domain' ),
        'icon'     => null,
        'position' => 1,
    );

    // Extract position from the custom category array.
    $position = $custom_category['position'];

    // Remove position from the custom category array.
    unset( $custom_category['position'] );

    // Insert the custom category at the desired position.
    array_splice( $categories, $position, 0, array( $custom_category ) );

    return $categories;
}

Start by creating a function and a new category array, much like you did in the previous example. But this time you will add a key for position letting the value specify where you want your new category to live inside $categories.

Then you will extract the position and set it as a variable to use in an array_splice, but before that, use unset to pull the position out of the array—you don’t need it.

Finally, use an array_splice function, which accepts 4 parameters:

  1. Array: the array you want to modify
  2. Offset: the position where you wish to insert
  3. Length: specify the number of items to remove after the offset
  4. Replacement: an array of elements to add in the specified position

In your array_splice, pass in $categories, the position you specified in your new category array, a length (in this case 0 as we do not want to remove anything), then your new category array.

Adding and positioning multiple new categories

Now let’s add and position some categories all at once. Here’s how you can insert two custom categories into $categories at positions two and four.

add_filter( 'block_categories_all', 'add_order_multiple_block_categories', 10, 2 );

function add_order_multiple_block_categories( $categories ) {
    $custom_categories = array(
        array(
            'slug'     => 'custom-category-1',
            'title'    => __( 'Custom Category 1', 'your-text-domain' ),
            'icon'     => null,
            'position' => 1,
        ),
        array(
            'slug'     => 'custom-category-2',
            'title'    => __( 'Custom Category 2', 'your-text-domain' ),
            'icon'     => null,
            'position' => 3,
        ),
    );

    $added_categories = array();

    // Prepare an associative array with positions as keys.
    foreach ( $custom_categories as $custom_category ) {
        $position = $custom_category['position'];
        unset( $custom_category['position'] );
        $added_categories[ $position ] = $custom_category;
    }

    // Sort the categories to insert by their positions/key.
    ksort( $added_categories );

    // Insert the sorted categories into the existing categories array.
    foreach ( $added_categories as $position => $custom_category ) {
        array_splice( $categories, $position, 0, array( $custom_category ) );
    }

    return $categories;
}

Create a function. Inside, create an array of new categories named $custom_categories, including the desired position for each item. Create an empty array to store data in the next step, name this $added_categories.

Next, iterate through $custom_categories to extract and unset the position and store it in $added_categories with the new position as its key. Then add a ksort function to make sure the categories that were stored in $added_categories are sorted by their keys.

Finally add foreach and iterate over the $added_categories and use an array_slice to insert each category into the desired position. Finally, reindex the categories using array_values to ensure things are in the correct order in the new category list.

Reordering categories

You can also use the block_categories_all hook to reorder the existing list of categories in the block editor. Whether you want to move a single category or rearrange multiple, here are some ways to get more control over both.

Reordering a single category

Here’s how to reorder the Design category in the existing array by removing it and reinserting it in the second position using array_splice PHP function.

add_filter( 'block_categories_all', 'reorder_design_category', 10, 2 );

function reorder_design_category( $categories ) {
    $design_category = null;
    $new_position    = 1;

    // Remove the design category from the existing categories.
    foreach ( $categories as $key => $category ) {
        if ( 'design' === $category['slug'] ) {
            $design_category = $category;
            unset( $categories[ $key ] );
            break;
        }
    }

    // If the design category exists, insert it at the new position.
    if ( $design_category ) {
        array_splice( $categories, $new_position, 0, array( $design_category ) );
    }

    // Reindex the array.
    return array_values( $categories );
}

Start by setting two variables inside your function, one named $design_category set as null that you’ll use later to store the design category, or whichever category you wish to move, and another named $position that specifies the position you want to move the category into. Then, add a foreach that will iterate through the existing categories to find a match for the category slug, store it in $design_category, and use unset to remove it from the existing list.

Now check if any data was stored in your variable and use array_splice to insert the category into the desired position, just as you did in the new category functions above. Finally, re-index the categories using array_values to ensure things are in the correct order.

Reordering multiple categories

Here’s how to reorder both the Design and Text categories to positions one and four by removing and reinserting them using array_splice.

<?php
add_filter( 'block_categories_all', 'reorder_multiple_categories', 10, 2 );

function reorder_multiple_categories( $categories ) {
    $reorder_categories = array(
        'design' => 0,
        'text'   => 3,
    );

    $moved_categories = array();

    // Iterate through the existing categories and add/remove the ones to be reordered.
    foreach ( $categories as $key => $category ) {
        if ( array_key_exists( $category['slug'], $reorder_categories ) ) {
            $moved_categories[ $reorder_categories[ $category['slug'] ] ] = $category;
            unset( $categories[ $key ] );
        }
    }

    // Sort the moved categories by their new positions.
    ksort( $moved_categories );

    // Insert the moved categories at their new positions.
    foreach ( $moved_categories as $position => $category ) {
        array_splice( $categories, $position, 0, array( $category ) );
    }

    // Reindex the array.
    $categories = array_values( $categories );

    return $categories;
}

Start by creating an associative array named $reorder_categories inside your function. The keys can be of the categories you want to reorder, and the value is their new position. You will also need to create an empty array to store data in the next step, name this $moved_categories.

When the check finds a match, the function adds the category to $moved_categories, in the position you specified as its key. The unset function drops the category from $categories.

Now add a ksort function to, well, sort the categories by their keys. 

Add a foreach to iterate over the $moved_categories, using array_splice to insert each category into its proper position. Finally, re-index the categories with array_values, so things are in the right order in the new $categories list.

Renaming categories

You can also use block_categories_all to rename the list of categories in the block editor. Want to rename one category? Want to rename them all? Here are some approaches that will give you control over the whole thing..

Renaming one category

Maybe you’d like to rename the Text category to Text Elements.

add_filter( 'block_categories_all', 'rename_text_category', 10, 2 );

function rename_text_category( $categories ) {
    foreach ( $categories as &$category ) {
        if ( $category['slug'] === 'text' ) {
            $category['title'] = __( 'Text Elements', 'your-text-domain' );
        }
    }

    return $categories;
}

Start by using a foreach. Iterate through $categories, looking for a category with a slug equal to text. If text exists, rename the category title to Text Elements.

Renaming multiple categories

Or maybe you really wanted to rename the Text and Media categories to Text Elements and Media and Text Elements.

add_filter( 'block_categories_all', 'rename_multiple_categories', 10, 2 );

function rename_multiple_categories( $categories ) {
    foreach ( $categories as &$category ) {
        if ( $category['slug'] === 'text' ) {
            $category['title'] = __( 'Text Elements', 'your-text-domain' );
        }
        if ( $category['slug'] === 'media' ) {
            $category['title'] = __( 'Media and Text Elements', 'your-text-domain' );
        }
    }

    return $categories;
}

To rename multiple categories in one function, follow the same steps as you did for renaming a single category. Only add as many checks for the category slug as you need.

Bringing it all together

Finally. Here you have it all— complete control over the names and position of existing categories, and the power to add new ones.

add_filter( 'block_categories_all', 'modify_block_categories', 10, 2 );

function modify_block_categories( $categories ) {
    $new_category_order = array(
        array(
            'slug'     => 'media',
            'title'    => __( 'Media and Text Elements', 'your-text-domain' ),
            'position' => 0,
        ),
        array(
            'slug'     => 'custom-blocks',
            'title'    => __( 'Custom Blocks', 'your-text-domain' ),
            'position' => 1,
        ),
        array(
            'slug'     => 'text',
            'title'    => __( 'Text Elements', 'your-text-domain' ),
            'position' => 2,
        ),
        array(
            'slug'     => 'embed',
            'position' => 3,
        ),
        array(
            'slug'     => 'design',
            'position' => 4,
        ),
    );

    // Create an associative array of block categories with the slug as the key.
    $current_block_categories = array_column( $categories, 'title', 'slug' );

    // Check if the new category order has a title set, otherwise use the default title.
    foreach ( $new_category_order as &$new_category ) {
        $new_category['title'] = $new_category['title'] ?? $current_block_categories[ $new_category['slug'] ] ?? __( 'Untitled', 'your-text-domain' );
    }

    // Create an array of slugs from the new category order.
    $new_category_slugs = array_column( $new_category_order, 'slug' );

    // Filter out the remaining block categories that are not in the new order.
    $remaining_categories = array_filter( $categories, function ( $category ) use ( $new_category_slugs ) {
        return ! in_array( $category['slug'], $new_category_slugs, true );
    } );

    // Merge the new category order with the remaining categories.
    return array_merge( $new_category_order, $remaining_categories );
}

Start by defining an array of categories you want to add, reorder or rename named $new_category_order. Now, the slug is required to identify each entry. The title and position are optional based on what you want to modify.

First, update any category titles by using array_column to create an associative array named $current_block_categories from $categories, using the category slug as the key. Then use a foreach to iterate through $new_category_order, checking whether or not a new title is set for any of the items in the array and updating them if necessary. If no new title is set, it will be extracted from $current_block_categories based on the slug. If for whatever reason no slug is found, then Untitled will be used.

Now build an associative array of categories by iterating over $new_category_order, checking for any categories with a new position, then adding it as the key in an array named $moved_categories

Then use ksort to sort the $moved_categories based on their position. Add a foreach and iterate over the $moved_categories and use an array_slice to insert each category into position.

Now that the new titles and positions are handled it’s time to filter out the remaining categories so you can bring everything back together. First, create an array of slugs named $new_category_order from array_column. Then create a variable named $remaining_categories that uses an array_filter to remove any items from $categories whose slugs do not exist in $new_category_order.

Finally, use an array_merge to combine the new category order with the $remaining_categories to return a full list of modified Block Categories.

Conclusion

The block_categories_all hook lets you make block categories work any way you need them to—for your project and for your users, and all the content they’ll create.

And, really, these functions are just the beginning. How are you going to use them? Do you have others, or can you see creative twists on them? Leave a comment!

Props to @psykro and @marybaum for reviewing this article and offering feedback.

2 responses to “One hook to rule them all: The many faces of block categories”

  1. Pramod Gaikwad Avatar

    Really amazing and simple to understand. Thanks.

  2. Diego Bittencourt Avatar

    Thanks for teaching the Community how to customize block categories, Chaplin.

Leave a Reply

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