Got a call last week from a client in a full-blown panic. Their marketing team was complaining that the block editor had been stripped bare, with only paragraphs and headings available. At the same time, their lead designer had a completely broken Site Editor: templates would not load and the layout blocks were gone. The designer blamed the marketers, the marketers blamed the designer. My hunch was that a dev fix had gone wrong.
It turned out a junior dev had been asked to limit allowed blocks for the content team so the blog post layouts stayed consistent. That is a reasonable request, and the solution looked obvious: the allowed_block_types_all filter. The problem is they applied it globally, and in modern WordPress that is a landmine.
Why a global filter breaks the Site Editor
The Site Editor is not editing a post or a page. It works with special post types, wp_template and wp_template_part, and it needs access to every layout block (groups, rows, stacks, query loops and the rest) to work at all. When you put a global filter on allowed_block_types_all, you are telling WordPress, “These are the only blocks anyone gets to use, anywhere.” So the Site Editor tries to load a template, finds the blocks it needs are banned, and breaks.
Years ago my first thought would have been the same, just use the filter. Before the Site Editor existed, that was fine. It is a good example of a fix from one era turning into a problem in the next. You cannot use a sledgehammer when the job needs a scalpel.
The right way: check the context
That filter is smarter than it looks. It passes a second argument, $context, which tells you exactly where the filter is being called, whether that is the post editor, a widget, or the Site Editor. Once you check the context, you can apply the rules only where you want them.
add_filter( 'allowed_block_types_all', 'allowed_blocks', 10, 2 );
function allowed_blocks( $allowed, $context ) {
// Site Editor usually edits these post types.
if ( isset( $context->post ) && $context->post ) {
$post_type = $context->post->post_type;
// Allow all blocks in the Site Editor.
if ( in_array( $post_type, array( 'wp_template', 'wp_template_part' ), true ) ) {
return true;
}
// Restrict blocks for regular content.
if ( in_array( $post_type, array( 'post', 'page' ), true ) ) {
return array(
'core/paragraph',
'core/heading',
'core/image',
);
}
}
// Fallback: detect the Site Editor screen directly if available.
if ( function_exists( 'get_current_screen' ) ) {
$screen = get_current_screen();
if ( $screen && 'site-editor' === $screen->id ) {
return true;
}
}
// Another fallback: some contexts expose a name like 'core/edit-site'.
if ( isset( $context->name ) && 'core/edit-site' === $context->name ) {
return true;
}
return $allowed;
}So what’s the point?
The lesson is not really about this one filter. It is about using the context WordPress hands you. A lot of modern hooks and filters pass that extra parameter for a reason. The code here is a slightly sturdier version of a snippet from the WordPress Developer News. Checking the context first saves hours of debugging.
- Leave the Site Editor alone: the code first checks whether we are in a
wp_templateorwp_template_partcontext. If so, it returnstrue, which tells WordPress to allow every block there. - Restrict for content editors: next it checks whether the post type is a standard
postorpage. If it is, then it returns the short list of approved blocks. - Add fallbacks: the checks for
get_current_screen()and$context->nameare there in case the post object is not available, so the function behaves consistently across WordPress versions.
This stuff gets complicated fast. If you are tired of debugging someone else’s mess and just want your site to work, drop my team a line. We have probably seen it before.