WordPress.org

WordPress Developer Blog

How to disable specific blocks in WordPress

Have you ever wanted to disable specific blocks in WordPress? Whether to simplify the editing experience, maintain consistency across a site, or any other reason, this article will guide you through the process. 

At its core, block restriction in WordPress can be accomplished with either PHP or JavaScript. The choice between these two depends largely on your existing site setup and personal preferences.

For instance, extending your JavaScript code to include block restrictions might be the most seamless route if you’re already enqueuing a JavaScript file for tasks like creating custom block variations or other Editor enhancements. On the other hand, PHP offers a server-side solution that is easy to add to existing plugins and themes already part of your environment.

Either option will give you control over the available blocks in the Editor. Let’s get started.

Configure personal preferences

While the primary focus of this article is disabling blocks with code, there is a no-code option as well. You can hide specific block types using the Editor’s preferences panel.

To do this in the Editor or Site Editor, click on the options menu, represented by three vertical dots at the screen’s top-right corner. From this menu, select Preferences. In the popup that appears, navigate to the Blocks section in the sidebar. Here, you’ll see a list of registered blocks. Check the ones you wish to hide.

These user-specific settings offer a convenient way to tailor the block inserter to your personal preferences, especially if there are blocks you never use. However, doing so will not restrict blocks for other users on your site. We will need some code for that.

Disable blocks with PHP

The first method uses PHP, specifically the  allowed_block_types_all hook. The callback function for this hook accepts two parameters:

  • $allowed_block_types – An array of block type slugs or boolean to enable or disable all blocks.
  • $block_editor_context – The current block editor context.

It’s important to note that when using this hook to disable blocks, any block patterns composed of disabled blocks will also become unavailable for the user. 

Using an allow list

Here’s a simple example that will only allow users to insert Heading, List, Image, and Paragraph blocks. This can be considered an “allow list.” All other blocks are disabled. 

To test the following code, add it to a theme’s functions.php file or include it in a plugin.

/**
 * Filters the list of allowed block types in the block editor.
 *
 * This function restricts the available block types to Heading, List, Image, and Paragraph only.
 *
 * @param array|bool $allowed_block_types Array of block type slugs, or boolean to enable/disable all.
 * @param object     $block_editor_context The current block editor context.
 *
 * @return array The array of allowed block types.
 */
function example_allowed_block_types( $allowed_block_types, $block_editor_context ) {

	$allowed_block_types = array(
		'core/heading',     // Heading block
		'core/image',       // Image block
		'core/list',        // List block
		'core/paragraph',   // Paragraph block
	);

	return $allowed_block_types;
}
add_filter( 'allowed_block_types_all', 'example_allowed_block_types', 10, 2 );

Here’s what the block Inserter will look like when this allow list is applied. 

Let’s make this example a bit more realistic with some conditionals.

You may only want to restrict Authors to these block types but give Editors and Administrators access to everything. You can use the current_user_can() function to do so. Update the code with the following.

/**
 * Filters the list of allowed block types in the block editor.
 *
 * This function restricts the available block types to Heading, List, Image, and Paragraph only
 * for users who do not have the 'publish_pages' capability. 
 *
 * @param array|bool $allowed_block_types Array of block type slugs, or boolean to enable/disable all.
 * @param object     $block_editor_context The current block editor context.
 *
 * @return array The array of allowed block types.
 */
function example_allowed_block_types_for_authors( $allowed_block_types, $block_editor_context ) {

	// If the current user doesn't have the correct permissions, restrict blocks.
	if ( ! current_user_can( 'publish_pages' ) ) {
		$allowed_block_types = array(
			'core/heading',     // Heading block
			'core/image',       // Image block
			'core/list',        // List block
			'core/paragraph',   // Paragraph block
		);

		return $allowed_block_types;
	}

	// The user has the correct permissions, so allow all blocks.
	return true;
}
add_filter( 'allowed_block_types_all', 'example_allowed_block_types_for_authors', 10, 2 );

Refer to the Plugin Handbook for further information on checking roles and capabilities. 

So far, we have not used the $block_editor_context parameter. This allows you to determine the characteristics of the Editor, such as the post type being edited, whether the user is working in the Editor or the Site Editor, and more. 

Let’s modify the code to restrict the insertable blocks for all users when editing posts, but only if the user is in the Editor. All blocks should be enabled if editing other post types or working in the Site Editor. 

For the post type slug, you can use $block_editor_context->post->post_type, and $block_editor_context->name will return the type of editor. Possible values are:

  • core/edit-post – Editor
  • core/edit-site – Site Editor
  • core/widgets – Widgets Editor
  • core/customize-widgets – Widgets Editor in the Customizer
/**
 * Filters the list of allowed block types based on the editing context.
 *
 * This function restricts the available block types to Heading, List, Image, and 
 * Paragraph when the user is editing posts in the Editor. When editing other post types
 * or in the Site Editor, all blocks are allowed.
 *
 * @param array|bool $allowed_block_types Array of block type slugs, or boolean to enable/disable all.
 * @param object     $block_editor_context The current block editor context, including the editor type and the post being edited.
 *
 * @return array|bool The array of allowed block types when editing posts, or true to allow all blocks in other contexts.
 */
function example_allowed_block_types_when_editing_posts( $allowed_block_types, $block_editor_context ) {

	// Only apply in the Editor when editing posts.
	if ( 
		'core/edit-post' === $block_editor_context->name &&
		isset( $block_editor_context->post ) && 
		'post' === $block_editor_context->post->post_type
	) {
		$allowed_block_types = array(
			'core/heading',     // Heading block
			'core/image',       // Image block
			'core/list',        // List block
			'core/paragraph',   // Paragraph block
		);

		return $allowed_block_types;
	}

	// Allow all blocks in the Site Editor or when editing other post types.
	return true;
}
add_filter( 'allowed_block_types_all', 'example_allowed_block_types_when_editing_posts', 10, 2 );

This approach can be helpful if your site utilizes custom post types designed to include only specific types of content.

Using a disallow list

Until now, we have provided a list of “allowed” blocks. While this works well in some situations, there are other scenarios where you might want to disable specific blocks instead. Perhaps your WordPress instance includes over a hundred blocks, and you wish to disable a few. 

Implementing a “disallow” list requires a bit less code in JavaScript, which we will cover later in this article, but it still can be done in PHP using the allowed_block_types_all hook. The key here is to programmatically fetch all the available registered block types and remove the ones you want to disable.

To accomplish this, use the WP_Block_Type_Registery class and the get_all_registered() method. 

Consider the following example to disable the Navigation and Query blocks for users who don’t have permission to edit_theme_options. Unless you have customized your WordPress installation, this will typically include only Administrators.

/**
 * Filters the list of allowed block types based on user capabilities.
 *
 * This function checks if the current user has the 'edit_theme_options' capability.
 * If the user does not have this capability, certain blocks are removed from the
 * list of allowed block types in the Editor.
 *
 * @param array|bool $allowed_block_types Array of block type slugs, or boolean to enable/disable all.
 * @param object     $block_editor_context The current block editor context.
 *
 * @return array The filtered list of allowed block types. If the current user does not have
 *               the 'edit_theme_options' capability, the list will exclude the disallowed blocks.
 */
function example_disallow_block_types( $allowed_block_types, $block_editor_context ) {

	// If the current user doesn't have the correct permissions, disallow blocks.
	if  ( ! current_user_can( 'edit_theme_options' ) ) {
		$disallowed_blocks = array(
			'core/navigation',
			'core/query',
		);
		
		// Get all registered blocks if $allowed_block_types is not already set.
		if ( ! is_array( $allowed_block_types ) || empty( $allowed_block_types ) ) {
			$registered_blocks   = WP_Block_Type_Registry::get_instance()->get_all_registered();
			$allowed_block_types = array_keys( $registered_blocks );
		}

		// Create a new array for the allowed blocks.
		$filtered_blocks = array();

		// Loop through each block in the allowed blocks list.
		foreach ( $allowed_block_types as $block ) {

			// Check if the block is not in the disallowed blocks list.
			if ( ! in_array( $block, $disallowed_blocks, true ) ) {

				// If it's not disallowed, add it to the filtered list.
				$filtered_blocks[] = $block;
			}
		}

		// Return the filtered list of allowed blocks
		return $filtered_blocks;
	}
	
	return $allowed_block_types;
}
add_filter( 'allowed_block_types_all', 'example_disallow_block_types', 10, 2 );

Like the earlier examples, you can adapt this approach even further. You can make the list conditional depending on various factors, such as specific post types or editors.

This demonstrates how a single PHP hook can be powerful for customizing the range of blocks available to users. Now, let’s focus on working with JavaScript.

Disable blocks with JavaScript

Implementing block restrictions through JavaScript requires a bit more setup, as it involves creating and enqueuing a JavaScript file in the Editor.

For this guide, I’ll assume the focus is adding block variations to a theme. However, you can adapt this method for use in a plugin. The primary difference is in the file locations for the functions. Let’s start by creating a file named restrict-blocks.js and placing it in the theme’s assets/js folder.

Next, enqueue restrict-blocks.js using the enqueue_block_editor_assets hook and the standard wp_equeue_script() function. Insert this code in your theme’s functions.php file unless you have a more advanced setup.

function example_enqueue_block_restrictions() {
	wp_enqueue_script(
		'example-enqueue-block-variations',
		get_template_directory_uri() . '/assets/js/restrict-blocks.js',
		array( 'wp-blocks', 'wp-dom-ready' ),
		wp_get_theme()->get( 'Version' ),
		false
	);
}
add_action( 'enqueue_block_editor_assets', 'example_enqueue_block_restrictions' );

Note that wp-blocks and wp-dom-ready are listed as dependencies. These will be used when creating restrictions in the following steps, but the initial setup is complete.

Using an allow list

In the PHP section above, creating an allow list was less complicated than a disallow list. In JavaScript, we have the reverse situation. 

Blocks are unregistered using the unregisterBlockType function, which accepts the name of the block. This function comes from the wp-blocks dependency that was added to the enqueue function earlier and can be called by prefixing the function with wp.block, or like this:

wp.domReady( () => {

	// Unregister the Verse block.
	wp.blocks.unregisterBlockType( 'core/verse' );
} );

Using wp.domReady is advised when unregistering blocks. Otherwise, the function may fire too early, never removing the block.

To selectively unregister blocks, leaving only a few, first retrieve a list of all existing block types using the getBlockTypes function. Once you have this list, you can iterate through it and unregister each block not included in your specified ‘allow list’. Here’s how to code this feature:

wp.domReady( () => {

	// Only allow the Heading, Image, List, and Paragraph blocks.
	const allowedBlocks = [
    		'core/heading',
    		'core/image',
    		'core/list',
    		'core/paragraph',
	];

	wp.blocks.getBlockTypes().forEach( function ( blockType ) {
		if ( allowedBlocks.indexOf( blockType.name ) === -1 ) {
			wp.blocks.unregisterBlockType( blockType.name );
		}
	} );
} );

Much like the PHP methods, you can also add conditionals. For example, the canUser selector from the @wordpress/data package lets you handle permissions, while the @wordpress/editor package gives you a convenient getCurrentPostType selector.

A simplified snippet shows how select can retrieve both selectors. This code lets you identify the current post type and see if the user can update settings (indicating Administrator privileges).

const { canUser } = wp.data.select( 'core' );
const { getCurrentPostType } = wp.data.select( 'core/editor' );

// Check user permissions and get the current post type.
const canUserUpdateSettings = canUser( 'update', 'settings' );
const currentPostType = getCurrentPostType();

You can then integrate these values into the allow list above. Here’s how to apply these block restrictions when the user is not an Administrator and when editing posts. 

wp.domReady( () => {
	const { canUser } = wp.data.select( 'core' );
	const { getCurrentPostType } = wp.data.select( 'core/editor' );

	// Check user permissions and get the current post type.
	const canUserUpdateSettings = canUser( 'update', 'settings' );
	const currentPostType = getCurrentPostType();

	// Only allow the Heading, Image, List, and Paragraph blocks.
	const allowedBlocks = [
    		'core/heading',
    		'core/image',
    		'core/list',
    		'core/paragraph',
	];

	if ( ! canUserUpdateSettings && currentPostType === 'post' ) {
		wp.blocks.getBlockTypes().forEach( function ( blockType ) {
			if ( allowedBlocks.indexOf( blockType.name ) === -1 ) {
				wp.blocks.unregisterBlockType( blockType.name );
			}
		} );
	}
} );

Using a disallow list

In cases where you need to disable only a few block types, employing a disallow list can be the most straightforward method. For example, if you wish to restrict non-administrators from using the Navigation and Query blocks while editing posts, you could structure the code like this:

wp.domReady( () => {
	const { canUser } = wp.data.select( 'core' );
	const { getCurrentPostType } = wp.data.select( 'core/editor' );

	// Check user permissions and get the current post type.
	const canUserUpdateSettings = canUser( 'update', 'settings' );
	const currentPostType = getCurrentPostType();

	// Only allow the Navigation and Query blocks.
	const disallowedBlocks = [
    		'core/navigation',
    		'core/query',
	];

	if ( ! canUserUpdateSettings && currentPostType === 'post' ) {
		forEach( disallowedBlocks as blockType ) {
			wp.blocks.unregisterBlockType( blockType );
		} );
	}
} );

JavaScript offers flexibility comparable to PHP, and it might even surpass it in some cases. This is because JavaScript operates directly within the Editor, granting access to additional settings and features specific to the Editor’s environment. However, delving into these advanced capabilities is beyond the scope of this article. 

Ultimately, the best approach—JavaScript or PHP—depends on your specific setup and requirements.

Disable block variations

While this article focuses on disabling and restricting blocks in the Editor, it would only be complete with some guidance on disabling block variations. Often, these variations can be mistaken for standalone blocks.

Consider the Row and Stack variations or the YouTube embed option. These appear as individual blocks in the Inserter but are, in fact, variations of the Group and Embed blocks, respectively. As a result, you cannot disable them using the previously outlined methods without disabling the entire Group and Embed blocks.

To manage block variations, leverage JavaScript, as this task has no PHP approach. Fortunately, disabling a specific block variation is straightforward. You can use the unregisterBlockVariation function in JavaScript by specifying the names of the original block and variation you wish to disable.

Add the following code to disable the Row, Stack, and YouTube variations in the JavaScript file created for the examples in the previous section.

wp.domReady( () => {
	wp.blocks.unregisterBlockVariation( 'core/group', 'group-row' );
	wp.blocks.unregisterBlockVariation( 'core/group', 'group-stack' );
	wp.blocks.unregisterBlockVariation( 'core/embed', 'youtube' );
});

For more information on block variations, refer to the article An introduction to block variations.

Disable blocks using the allowedBlocks attribute

The final method we’ll discuss allows block insertion on a global level. You can indicate which blocks can be inserted into the containers using the allowedBlocks attribute, container blocks, and HTML.

As of WordPress version 6.4, there are four types of blocks that support the allowedBlocks attribute: Column, Cover, Media & Text, and Group. The Group block category also encompasses Row, Stack, and Grid variations. Use the allowedBlocks attribute to specify an array of block names permitted inside the container block. Consequently, any block types not included in this array will be unavailable for insertion within that container.

For example, if you want to allow only Heading and Paragraph blocks within a specific container block, you would use the following specification.

"allowedBlocks":["core/heading","core/paragraph"]

Pair predefined patterns with block locking for enhanced control of your layouts, as there is no user interface for setting the allowedBlocks attribute in the Editor. 

To illustrate how the allowedBlocks attribute functions, let’s examine the “Call to action with image on right” pattern in the Twenty Twenty-Four theme. The pattern is structured as follows:

This pattern consists of a Group block serving as a wrapper that encloses a Columns block divided into two individual Column blocks. The intention is to limit the types of blocks that can be added to each column.

In this example, the left column is configured to accept only Buttons, Heading, List, and Paragraph blocks. The right column will only accept Image blocks.

// Left Column
"allowedBlocks":["core/buttons","core/heading","core/list","core/paragraph"]

// Right Column
"allowedBlocks":["core/image"]

Here’s a simplified version of the pattern focused on the Columns block markup with the attributes applied. 

<!-- wp:column {"allowedBlocks":["core/buttons","core/heading","core/list","core/paragraph"],"verticalAlignment":"center","width":"50%"} -->
<div class="wp-block-column is-vertically-aligned-center" style="flex-basis:50%">[...]
</div>
<!-- /wp:column -->

<!-- wp:column {"allowedBlocks":["core/image"],"verticalAlignment":"center","width":"50%"} -->
<div class="wp-block-column is-vertically-aligned-center" style="flex-basis:50%">[...]
</div>
<!-- /wp:column -->
View the complete pattern
<?php
/**
 * Title: Call to action with image on right (with block restrictions)
 * Slug: twentytwentyfour/cta-content-image-on-right
 * Categories: call-to-action, banner
 * Viewport width: 1400
 */
?>

<!-- wp:group {"align":"full","style":{"spacing":{"padding":{"top":"var:preset|spacing|50","bottom":"var:preset|spacing|50","left":"var:preset|spacing|50","right":"var:preset|spacing|50"},"margin":{"top":"0","bottom":"0"}}},"layout":{"type":"constrained"}} -->
<div class="wp-block-group alignfull" style="margin-top:0;margin-bottom:0;padding-top:var(--wp--preset--spacing--50);padding-right:var(--wp--preset--spacing--50);padding-bottom:var(--wp--preset--spacing--50);padding-left:var(--wp--preset--spacing--50)">
	<!-- wp:columns {"verticalAlignment":"center","align":"wide","style":{"spacing":{"blockGap":{"top":"var:preset|spacing|50","left":"var:preset|spacing|50"}}}} -->
	<div class="wp-block-columns alignwide are-vertically-aligned-center">
		<!-- wp:column {"allowedBlocks":["core/paragraph","core/buttons","core/heading","core/list","core/paragraph"],"verticalAlignment":"center","width":"50%"} -->
		<div class="wp-block-column is-vertically-aligned-center" style="flex-basis:50%">
			<!-- wp:heading -->
			<h2 class="wp-block-heading"><?php echo esc_html_x( 'Enhance your architectural journey with the Études Architect app.', 'Sample heading', 'twentytwentyfour' ); ?></h2>
			<!-- /wp:heading -->

			<!-- wp:list {"style":{"typography":{"lineHeight":"1.75"}},"className":"is-style-checkmark-list"} -->
			<ul class="is-style-checkmark-list" style="line-height:1.75">
				<!-- wp:list-item -->
				<li><?php echo esc_html_x( 'Collaborate with fellow architects.', 'Sample list item', 'twentytwentyfour' ); ?></li>
				<!-- /wp:list-item -->

				<!-- wp:list-item -->
				<li><?php echo esc_html_x( 'Showcase your projects.', 'Sample list item', 'twentytwentyfour' ); ?></li>
				<!-- /wp:list-item -->

				<!-- wp:list-item -->
				<li><?php echo esc_html_x( 'Experience the world of architecture.', 'Sample list item', 'twentytwentyfour' ); ?></li>
				<!-- /wp:list-item -->
			</ul>
			<!-- /wp:list -->

			<!-- wp:buttons -->
			<div class="wp-block-buttons">
				<!-- wp:button -->
				<div class="wp-block-button">
					<a class="wp-block-button__link wp-element-button"><?php echo esc_html_x( 'Download app', 'Button text of this section', 'twentytwentyfour' ); ?></a>
				</div>
				<!-- /wp:button -->

				<!-- wp:button {"className":"is-style-outline"} -->
				<div class="wp-block-button is-style-outline">
					<a class="wp-block-button__link wp-element-button"><?php echo esc_html_x( 'How it works', 'Button text of this section', 'twentytwentyfour' ); ?></a>
				</div>
				<!-- /wp:button -->
			</div>
			<!-- /wp:buttons -->
		</div>
		<!-- /wp:column -->

		<!-- wp:column {"allowedBlocks":["core/image"],"verticalAlignment":"center","width":"50%"} -->
		<div class="wp-block-column is-vertically-aligned-center" style="flex-basis:50%">
			<!-- wp:image {"aspectRatio":"4/3","scale":"cover","sizeSlug":"full","linkDestination":"none","className":"is-style-rounded"} -->
			<figure class="wp-block-image size-full is-style-rounded">
				<img src="<?php echo esc_url( get_template_directory_uri() ); ?>/assets/images/abstract-geometric-art.webp" alt="<?php esc_attr_e( 'White abstract geometric artwork from Dresden, Germany', 'twentytwentyfour' ); ?>" style="aspect-ratio:4/3;object-fit:cover" />
			</figure>
			<!-- /wp:image -->
		</div>
		<!-- /wp:column -->
	</div>
	<!-- /wp:columns -->
</div>
<!-- /wp:group -->

Now, when a user adds this pattern to a post, the blocks that can be inserted into each column are restricted accordingly. This strategy allows for highly tailored editing experiences, especially when combined with other curation methods. Such an approach can benefit projects where consistency and a prescribed content structure are essential.

It’s important to note that since an attribute controls this block restriction, users could technically switch to the Code Editor, remove the attribute, and then could add any blocks they desire. If this concerns you,  restrict access to the Code Editor using PHP. The necessary code snippet is available in the documentation.

Your WordPress, your way

As we wrap up this exploration of restricting blocks in WordPress, it’s clear that the platform offers a remarkable degree of flexibility and control. Whether through PHP, JavaScript, or the user preferences in the Editor itself, WordPress empowers you to tailor the content creation experience. 

As WordPress continues to evolve, so too will the Editor curation methods. If you are interested in this topic, I recommend the following blog articles and documentation.  

By thoughtfully applying these techniques, you can create a more streamlined, efficient, and enjoyable editing environment for yourself and your users. 

Props to @greenshady and @dansoschin for feedback and editorial review.

3 responses to “How to disable specific blocks in WordPress”

  1. Cullen Whitmore Avatar

    This is a great post. I especially like your example of restricting blocks in the Editor but not the Site Editor.

    From an agency standpoint of building a custom theme for a client, do you consider it better to add this restriction at the theme level or in a separate plugin?

    I see a potential con with it in the theme when needing to troubleshoot and having to edit the theme to test. However, I also see a potential con where the client could disable the plugin.

    1. Nick Diego Avatar

      One option could be a “must use” (mu) plugin. That way the client could not disable it by accident. While you can definitely put these restrictions in a theme, I would personally default to a utility plugin as this will be easier to maintain and deploy on multiple clients sites as needed.

  2. Alleknalle Avatar
    Alleknalle

    Great article for restricting blocks.

    What is important to know, is when you’re using the filter ‘allowed_block_types_all’, that you have to return a sequential array that would be in consecutive order with no gaps in the indices. If there are gaps in the indices (for example when you unset an index), you will get an error when you go to ‘preferences’ -> ‘blocks’ in the editor. This is because somewhere in javascript the array then gets transformed to an object and it can not use ‘.includes()’ on an object.

    So make sure you return a true sequentials array (eg: by using ‘array_values()’) when you filter on ‘allowed_block_types_all’

Leave a Reply

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