With each new WordPress release, the Editor’s design tools give users more power and flexibility, adding ever more ways to make content truly their own. But that power can make it tough to keep a site on brand visually and on track with an organization’s content guidelines. And the more people working on the site, the tougher that job gets.
That’s why, at the same time WordPress has been expanding users’ options, the project has been actively introducing methods to curate the Editor experience. This helps ensure that the Editor is used in a way that aligns with the goals and objectives of the site’s owners. Some industries call these curation techniques “governance.”
Today, governing the content of a website has gotten easier thanks to a new client-side filter introduced in WordPress 6.2. It’s called the blockEditor.useSetting.before
filter, and it puts a level of curation in your hands that has never been possible until now.
This article will guide you through the details and explore these new capabilities.
Table of Contents
Getting started with client-side filters
The blockEditor.useSetting.before
filter is a client-side JavaScript filter that lets you set block-level theme.json settings before the Editor is rendered. And, since it operates in the client, you get much more control than you have with server-side PHP filters. For example, you can easily modify settings based on a block’s relationship to other blocks.
To begin, first import addFilter
from the @wordpress/hooks package. The syntax for the function looks like this:
import { addFilter } from '@wordpress/hooks';
addFilter( 'hookName', 'namespace', callback, priority );
JavaScript filters in WordPress look and work a lot like PHP filters, but they do require a namespace as the second argument. The namespace uniquely identifies the callback function and can be anything you want. Priority is optional, so this article will ignore it for simplicity.
The callback function for the blockEditor.useSetting.before
filter accepts four parameters.
settingValue
– The current value of the block setting being filtered.settingName
– The name of the block setting to modify.clientId
– The unique identifier for the block.blockName
– The name of the block type.
Here’s an example of how to restrict the spacing units for Column blocks to just pixels. The spacing.units
setting is filtered, and the callback checks the blockName
parameter to see if the current block is a Column. If it is, the function returns an array of [ 'px' ]
. If not, it returns the current settingValue
.
import { addFilter } from '@wordpress/hooks';
function restrictColumnSpacingSettings(
settingValue,
settingName,
clientId,
blockName
) {
if (
blockName === 'core/column' &&
settingName === 'spacing.units'
) {
return [ 'px' ];
}
return settingValue;
}
addFilter(
'blockEditor.useSetting.before',
'block-curation-examples/useSetting.before/column-spacing',
restrictColumnSpacingSettings
);
While you can make this simple change with server-side theme.json filters, or directly in a theme’s theme.json file, the blockEditor.useSetting.before
filter is unique because it allows you to modify settings in many other ways: according to the block’s location, neighboring blocks, the current user’s role, and more.
Just remember these two things when you use this filter:
- You can only modify block settings that theme.json can define.
- The filter will not generate new CSS variables.
The filter cannot change block attributes directly—only block settings you can define at the block level in theme.json. Many Core, and most custom, blocks include settings you can’t configure in theme.json, meaning you can’t change them with the blockEditor.useSetting.before
filter.
When you define colors, font sizes, spacing, and more in theme.json, under the hood, you’re actually creating CSS variables that are then used to apply those settings. Again, the blockEditor.useSetting.before
filter will not generate new CSS variables. So if you use the filter to define a color palette for a block, the colors will only work if they have already been defined in theme.json. Think of theme.json as the source of all block settings.
But even with those limitations, this client-side filter’s power and flexibility can be truly impressive. Let’s look at a few more advanced examples.
All examples in this article were designed for the Twenty Twenty-Three theme, even though most are theme-agnostic. If you see a list of font sizes or a color palette here, know they have already been defined in theme.json.
Restricting settings by block attributes
Say you want to restrict the typography settings in Heading blocks based on their levels. For instance, you may grant H1 and H2 full access to all typography settings, but limit H3 through H6 to just three font sizes and disable all the other settings. You can’t do that in theme.json. But it’s simple with the blockEditor.useSetting.before
filter.
The level of a Heading block is saved as an attribute called level
. So you can add a check for the current heading level in the course of filtering the typography settings. If the level
is greater than or equal to 3, modify each settingValue
accordingly.
Before you see how to add the heading-level check, let’s review the initial setup for disabling typography settings, except for the three predefined font sizes, across all Heading blocks. Remember that the example below assumes the Twenty Twenty-Three theme, where the font sizes are already defined in theme.json.
import { addFilter } from '@wordpress/hooks';
function restrictHeadingTypographySettings(
settingValue,
settingName,
clientId,
blockName
) {
if ( blockName === 'core/heading' ) {
// Modify these block settings.
const modifiedBlockSettings = {
'typography.customFontSize': false,
'typography.fontStyle': false,
'typography.fontWeight': false,
'typography.letterSpacing': false,
'typography.lineHeight': false,
'typography.textDecoration': false,
'typography.textTransform': false,
'typography.fontSizes': [
{
fluid: {
min: '1rem',
max: '1.125rem',
},
size: '1.125rem',
slug: 'medium',
},
{
fluid: {
min: '1.75rem',
max: '1.875rem',
},
size: '1.75rem',
slug: 'large',
},
{
fluid: false,
size: '2.25rem',
slug: 'x-large',
},
],
};
if ( modifiedBlockSettings.hasOwnProperty( settingName ) ) {
return modifiedBlockSettings[ settingName ];
}
}
return settingValue;
}
addFilter(
'blockEditor.useSetting.before',
'block-curation-examples/useSetting.before/heading-typography',
restrictHeadingTypographySettings
);
You can design a callback function in lots of ways. I chose to create an object that maps setting names to modified values. This way, when a value is being filtered, the callback function checks if the settingName
of the value is in the object. If it is, the modified value is returned.
To limit these modifications to levels H3 through H6, you need to get the block’s current level
attribute with getBlockAttributes
, available from the @wordpress/block-editor package. That selector accepts a single parameter for the block’s unique client ID, which is already provided to the callback function as clientId
.
Here’s a simplified code snippet that shows how you can use select
to return the getBlockAttributes
selector from the 'core/block-editor'
store and then use it to retrieve the level
attribute for the selected block.
import { select } from '@wordpress/data';
const { getBlockAttributes } = select( 'core/block-editor' );
// Determine the level of the block based on its client ID.
const headingLevel = getBlockAttributes( clientId )?.level ?? 0;
The headingLevel
constant looks complicated, but it retrieves the block attributes object and checks if the level
property is present. If it is, the current value is returned. Otherwise, 0 is returned. The ??
is called a nullish coalescing operator.
Putting it all together, the complete filter should look something like this:
import { select } from '@wordpress/data';
import { addFilter } from '@wordpress/hooks';
function restrictHeadingTypographySettings(
settingValue,
settingName,
clientId,
blockName
) {
if ( blockName === 'core/heading' ) {
const { getBlockAttributes } = select( 'core/block-editor' );
// Determine the level of the 'core/heading' block.
const headingLevel = getBlockAttributes( clientId )?.level ?? 0;
// Modify these block settings.
const modifiedBlockSettings = {
'typography.customFontSize': false,
'typography.fontStyle': false,
'typography.fontWeight': false,
'typography.letterSpacing': false,
'typography.lineHeight': false,
'typography.textDecoration': false,
'typography.textTransform': false,
'typography.fontSizes': [
{
fluid: {
min: '1rem',
max: '1.125rem',
},
size: '1.125rem',
slug: 'medium',
},
{
fluid: {
min: '1.75rem',
max: '1.875rem',
},
size: '1.75rem',
slug: 'large',
},
{
fluid: false,
size: '2.25rem',
slug: 'x-large',
},
],
};
// Only apply setting modifications to H3-H6.
if (
headingLevel >= 3 &&
modifiedBlockSettings.hasOwnProperty( settingName )
) {
return modifiedBlockSettings[ settingName ];
}
}
return settingValue;
}
addFilter(
'blockEditor.useSetting.before',
'block-curation-examples/useSetting.before/heading-typography',
restrictHeadingTypographySettings
);
In the Editor, the available typography settings will be automatically adjusted based on the level of the Heading block.

While this example may not be relevant to your Editor curation needs, it hints at the potential of the blockEditor.useSetting.before
filter.
Next, let’s consider user permissions and conditional settings based on post type.
Restricting settings by user permissions and post type
While the Editor gives users an unparalleled level of design control for blocks, this level of freedom is not always a great thing—especially when your website has a lot of users with a wide range of roles. Administrators might need access to all the design tools, but content creators probably don’t.
In theme.json, you can restrict settings globally or at the block level. That’s great for disabling specific functionality throughout the site. But you’ll need to look elsewhere for a user-level management system for these restrictions. There are theme.json server-side PHP filters available, but those are beyond the scope of this article.
For now, maybe you want to disable border settings for everyone but Administrators, and only for posts. Border settings should stay enabled for all users when they’re editing pages or some other custom post types.
You can use the blockEditor.useSetting.before
filter along with two additional tools to get this done. The canUser
selector from the @wordpress/data package lets you handle permissions, while the @wordpress/editor package gives you a convenient getCurrentPostType
selector.
As before, here’s a simplified snippet that shows you how select
can retrieve both selectors. This code allows you to identify the current post type and see if the current user can update settings (indicating Administrator privileges).
import { select } from '@wordpress/data';
const { canUser } = select( 'core' );
const { getCurrentPostType } = select( 'core/editor' );
// Check user permissions and get the current post type.
const canUserUpdateSettings = canUser( 'update', 'settings' );
const currentPostType = getCurrentPostType();
When you combine the power of these selectors, it’s easy to use conditional logic to enable or disable block settings based on user roles and the post type being edited.
Putting it all together, the complete filter should look something like this:
import { select } from '@wordpress/data';
import { addFilter } from '@wordpress/hooks';
function restrictBlockSettingsByUserPermissionsAndPostType(
settingValue,
settingName,
clientId,
blockName
) {
const { canUser } = select( 'core' );
const { getCurrentPostType } = select( 'core/editor' );
// Check user permissions and get the current post type.
const canUserUpdateSettings = canUser( 'update', 'settings' );
const currentPostType = getCurrentPostType();
// Disable block settings on these post types.
const disabledPostTypes = [ 'post' ];
// Disable these block settings.
const disabledBlockSettings = [
'border.color',
'border.radius',
'border.style',
'border.width',
];
if (
! canUserUpdateSettings &&
disabledPostTypes.includes( currentPostType ) &&
disabledBlockSettings.includes( settingName )
) {
return false;
}
return settingValue;
}
addFilter(
'blockEditor.useSetting.before',
'block-curation-examples/useSetting.before/user-permissions-and-post-type',
restrictBlockSettingsByUserPermissionsAndPostType
);
Now, notice that I took a different approach to modifying the settingValue
in this callback function. All four border settings must be false
when the conditions are met, so all I needed was a straightforward array containing each settingName
.
Here’s what this applied filter will look like in the Editor for users with different permissions.


Restricting settings based on block context
It was the need to define block settings in context that partially motivated the development of blockEditor.useSetting.before
filter. Consider the following example.
Suppose you have a specific style guide for buttons. When users add a button to a post or page, they should have access to the theme’s complete color palette. But when they place a button in a Cover block, their color choices should only be black or white. Essentially, you need to restrict the color settings for Button blocks based on their location or, in other words, on their context.
Let’s ignore the contextual requirements for a second. Here’s how you would restrict the color settings for all Button blocks.
import { addFilter } from '@wordpress/hooks';
function restrictButtonBlockSettingsByLocation(
settingValue,
settingName,
clientId,
blockName
) {
if ( blockName === 'core/button' ) {
// Modify these block settings.
const modifiedBlockSettings = {
'color.custom': false,
'color.customGradient': false,
'color.defaultGradients': false,
'color.defaultPalette': false,
'color.gradients.theme': [],
'color.palette.theme': [
{
color: '#ffffff',
name: 'Base',
slug: 'base',
},
{
color: '#000000',
name: 'Contrast',
slug: 'contrast',
},
],
};
return modifiedBlockSettings[ settingName ];
}
return settingValue;
}
addFilter(
'blockEditor.useSetting.before',
'block-curation-examples/useSetting.before/button-location',
restrictButtonBlockSettingsByLocation
);
The code above will disable custom colors and gradients while simplifying the color palette to only black (Contrast) and white (Base). This callback function should look similar to the first example in this article, where a modifiedBlockSettings
object contains the setting names and their new values.
To apply these modifications based on the context of the Button block, you will need to check if the block has any parents. If it does, you can get the names of those parent blocks, and if one of the parents is a Cover, then apply the restrictions.
Thankfully, the @wordpress/block-editor package gives you the two selectors you’ll need: getBlockParents
and getBlockName
. Again, you will use select
to retrieve both.
import { select } from '@wordpress/hooks';
const { getBlockParents, getBlockName } = select( 'core/block-editor' );
// Get the block's parents and see if one is a 'core/cover' block.
const blockParents = getBlockParents( clientId, true );
const inCover = blockParents.some(
( parentId ) => getBlockName( parentId ) === 'core/cover'
);
Note that the getBlockParents
selector accepts two parameters: the block client ID and a boolean parameter that specifies whether to return the block parents in ascending (true) or descending (false) order. In this example, the order is irrelevant.
With isCover
defined, you can tell if the Button block is inside a Cover block. Putting it all together, the final code for the filter should look something like this:
import { select } from '@wordpress/data';
import { addFilter } from '@wordpress/hooks';
function restrictButtonBlockSettingsByLocation(
settingValue,
settingName,
clientId,
blockName
) {
if ( blockName === 'core/button' ) {
const { getBlockParents, getBlockName } = select( 'core/block-editor' );
// Get the block's parents and see if one's a 'core/cover' block.
const blockParents = getBlockParents( clientId, true );
const inCover = blockParents.some(
( parentId ) => getBlockName( parentId ) === 'core/cover'
);
// Modify these block settings.
const modifiedBlockSettings = {
'color.custom': false,
'color.customGradient': false,
'color.defaultGradients': false,
'color.defaultPalette': false,
'color.gradients.theme': [],
'color.palette.theme': [
{
color: '#ffffff',
name: 'Base',
slug: 'base',
},
{
color: '#000000',
name: 'Contrast',
slug: 'contrast',
},
],
};
if ( inCover && modifiedBlockSettings.hasOwnProperty( settingName ) ) {
return modifiedBlockSettings[ settingName ];
}
}
return settingValue;
}
addFilter(
'blockEditor.useSetting.before',
'block-curation-examples/useSetting.before/button-location',
restrictButtonBlockSettingsByLocation
);
And here is a before-and-after view of the Editor with this filter applied.


Now that you know how to modify settings based on context, you can combine this example with user permissions, block attributes, or post types. There are so many ways to curate the Editor experience using this single filter.
For more information and a working plugin that explores the examples in this article, you can check out the Editor Curation Examples plugin on GitHub. The Block Editor Handbook also includes a fantastic resource of additional curation methods, and more are being added with each WordPress release.
So what will you build using the blockEditor.useSetting.before
filter? Is there functionality that’s missing that you would like to see? Add your thoughts in the comments.
Props to @marybaum, @mburridge, @bph, @alecgeatches, @ingeniumed, and @greenshady for feedback and review.
Leave a Reply