Leaner, steadier PHPUnit runs for upcoming releases

In recent years, the CI load across coreCore Core is the set of software required to run WordPress. The Core Development Team builds WordPress. WordPress GitHubGitHub GitHub is a website that offers online implementation of git repositories that can easily be shared, copied and modified by other developers. Public repositories are free to host, private repositories require a paid subscription. GitHub introduced the concept of the ‘pull request’ where code changes done in branches by contributors can be reviewed and discussed before being merged by the repository owner. https://github.com/ repos increased as the project evolved. This is normal for an active project.

But, the load can be especially high when running the full PHPUnit matrix at once on many PRs, such as during a release with many backportbackport A port is when code from one branch (or trunk) is merged into another branch or trunk. Some changes in WordPress point releases are the result of backporting code from trunk to the release branch. branches. Cases like this mean the number of jobs can strain the capacity of the system.

Ahead of 7.1, we’ve landed a round of trims and reliability fixes aimed at increasing capacity and running leaner while ensuring quality checks still run.

This CI work builds on the test-suite optimization work from many contributors in the past.

Two key changes

  1. Trimmed the PHPUnit matrix to boundary PHPPHP The web scripting language in which WordPress is primarily architected. WordPress requires PHP 7.4 or higher versions (#12719 on trunktrunk A directory in Subversion containing the latest development code in preparation for the next major release cycle. If you are running "trunk", then you are on the latest revision., #12720 for 7.0). Full PHP coverage kept, redundant database combinations dropped. Per run: ~52% fewer jobs and ~54% fewer job-minutes.
  2. Fetch the GutenbergGutenberg The Gutenberg project is the new Editor Interface for WordPress. The editor improves the process and experience of creating new content, making writing rich content much simpler. It uses ‘blocks’ to add richness rather than shortcodes, custom HTML etc. https://wordpress.org/gutenberg/ build once per run (#12701) instead of once per job, plus bounded retries on Docker image pulls (#12703). Runs needing a rerun to go green roughly halved, from ~68% to ~36%.

Source: measured per run from the GitHub Actions APIAPI An API or Application Programming Interface is a software intermediary that allows programs to interact with each other and share data in limited, clearly defined ways. (job counts and durations, and rerun counts) across comparable runs before / after the changes.

Next up

  • Trim the 6.8 matrix (#12726).
  • Pilot a dedicated larger-runner pool for use during release windows to help with concurrency issues (more on that in a future update).

Note: The PHPUnit tests themselves could be further tuned. The trims above cut job count, not job duration—so we could give more attention to the tests themselves to find more efficiency.

Thanks to contributions from adrianmoldovanwp, barry, garyj, @johnbillion, @jonsurrell, @jorbin, lucasbustamante, and @mukesh27.

jQuery UI updated to 1.14.2 in WordPress 7.1

WordPress 7.1 includes an update to the latest stable version of jQuery UI, 1.14.2 from version 1.13.3. This update drops support for all versions of Internet Explorer & Edge Legacy which is inline with the WordPress Browser Support Policy.

This update sets jQuery.uiBackCompat to be equal to true to ensure that code written for the jQuery 1.11 APIAPI An API or Application Programming Interface is a software intermediary that allows programs to interact with each other and share data in limited, clearly defined ways. continues to function as expected. Additionally, $.fn._form, $.ui.ie, $.ui.safeActiveElement, and $.ui.safeBlur have been removed. WordPress coreCore Core is the set of software required to run WordPress. The Core Development Team builds WordPress. does not use any of these functions, but you should check your code and update it accordingly if it does.

See [62747] for the specific changes and #62757 for more background information.

props @masteradhoc and @joedolson for review

#7-1, #dev-notes, #dev-notes-7-1, #external-libraries

New execution lifecycle filters for the Abilities API in WordPress 7.1

WordPress 7.1 introduces four filters that allow plugins to customise the execution lifecycle of abilities registered with the Abilities APIAPI An API or Application Programming Interface is a software intermediary that allows programs to interact with each other and share data in limited, clearly defined ways..

The Abilities API previously provided the wp_before_execute_ability and wp_after_execute_ability actions. These actions are useful for observing execution, but they cannot change its behaviour.

The new filters allow developers to:

  • Short-circuit ability execution.
  • Transform normalised input.
  • Apply additional authorisation rules.
  • Transform or recover an execution result.

These changes were introduced in TracTrac An open source project by Edgewall Software that serves as a bug tracker and project management tool for WordPress. ticketticket Created for both bug reports and feature development on the bug tracker. #64989 and changeset [62397].

Updated execution lifecycle

The filters are applied in the following order:

wp_pre_execute_ability
        │
        ├── short-circuit when an override is returned
        ↓
WP_Ability::normalize_input()
        ↓
wp_ability_normalize_input
        ↓
WP_Ability::validate_input()
        ↓
WP_Ability::check_permissions()
        ↓
wp_ability_permission_result
        ↓
wp_before_execute_ability
        ↓
Registered execute callback
        ↓
wp_ability_execute_result
        ↓
WP_Ability::validate_output()
        ↓
wp_after_execute_ability
        ↓
Return result

Input and output transformations occur before their respective schema-validation steps. Transformed values must therefore continue to satisfy the ability’s registered schemas.

The exception is wp_pre_execute_ability, which bypasses the rest of the pipeline completely.

Short-circuiting execution with wp_pre_execute_ability

The wp_pre_execute_ability filterFilter Filters are one of the two types of Hooks https://codex.wordpress.org/Plugin_API/Hooks. They provide a way for functions to modify data of other functions. They are the counterpart to Actions. Unlike Actions, filters are meant to work in an isolated manner, and should never have side effects such as affecting global variables and output. runs at the beginning of WP_Ability::execute(), before input normalization, validation or permission checks.

/**
 * Filters whether to short-circuit ability execution.
 *
 * @param mixed      $pre          Precomputed result. Return it unchanged to
 *                                 continue normal execution.
 * @param string     $ability_name Name of the ability.
 * @param mixed      $input        Raw input passed to execute().
 * @param WP_Ability $ability      Ability instance.
 */
apply_filters(
    'wp_pre_execute_ability',
    $pre,
    $ability_name,
    $input,
    $ability
);

Returning $pre unchanged allows execution to continue. Returning any other value short-circuits execution and returns that value directly to the caller.

The filter uses a unique internal sentinel as its default. This means that any PHPPHP The web scripting language in which WordPress is primarily architected. WordPress requires PHP 7.4 or higher value—including null, false, or an object—can be used as a legitimate short-circuit result.

The filter can be used for caching, rate limiting, maintenance mode, approval workflows and test mocking.

Temporarily disabling an ability

The filter can short-circuit selected abilities during maintenance without running input validation, permission checks, or the registered callback:

add_filter(
    'wp_pre_execute_ability',
    function ( $pre, $ability_name, $input, $ability ) {
        if ( 'my-plugin/sync-catalog' !== $ability_name ) {
            return $pre;
        }

        if ( ! get_option( 'my_plugin_maintenance_mode', false ) ) {
            return $pre;
        }

        return new WP_Error(
            'ability_temporarily_unavailable',
            __( 'This operation is temporarily unavailable due to maintenance.', 'my-plugin' ),
            array(
                'status' => 503,
            )
        );
    },
    10,
    4
);

Returning $pre unchanged continues normal execution. When maintenance mode is enabled, the WP_Error is returned immediately, and the remaining ability pipeline is bypassed.

Because this filter runs before permission checks and validation, it should make only narrow decisions that do not depend on validated input or the current ability authorisation result.

Transforming input with wp_ability_normalize_input

The wp_ability_normalize_input filter runs inside WP_Ability::normalize_input(), after the method has applied any defaults declared by the input schema.

/**
 * Filters normalized ability input.
 *
 * @param mixed      $input        Normalized input.
 * @param string     $ability_name Name of the ability.
 * @param WP_Ability $ability      Ability instance.
 */
apply_filters(
    'wp_ability_normalize_input',
    $input,
    $ability_name,
    $ability
);

This filter can be used to:

  • Add defaults that cannot be expressed through JSONJSON JSON, or JavaScript Object Notation, is a minimal, readable format for structuring data. It is used primarily to transmit data between a server and web application, as an alternative to XML. Schema.
  • Normalize incoming values.
  • Enrich an AI prompt.
  • Inject caller or execution-context metadata.

Adding contextual input

add_filter(
    'wp_ability_normalize_input',
    function ( $input, $ability_name, $ability ) {
        if ( 'my-plugin/process-content' !== $ability_name ) {
            return $input;
        }

        if ( ! is_array( $input ) ) {
            $input = array();
        }

        $input['requesting_user_id'] = get_current_user_id();
        $input['site_url']           = home_url();

        return $input;
    },
    10,
    3
);

The transformed input is subsequently checked against the ability’s input_schema.

Returning a WP_Error stops execution before input validation, permission checks and the registered callback:

add_filter(
    'wp_ability_normalize_input',
    function ( $input, $ability_name ) {
        if ( 'my-plugin/process-content' !== $ability_name ) {
            return $input;
        }

        if ( my_plugin_rate_limit_exceeded() ) {
            return new WP_Error(
                'ability_rate_limit_exceeded',
                __( 'The ability rate limit has been exceeded.', 'my-plugin' ),
                array( 'status' => 429 )
            );
        }

        return $input;
    },
    10,
    2
);

When execution occurs through the Abilities REST APIREST API The REST API is an acronym for the RESTful Application Program Interface (API) that uses HTTP requests to GET, PUT, POST and DELETE data. It is how the front end of an application (think “phone app” or “website”) can communicate with the data store (think “database” or “file system”) https://developer.wordpress.org/rest-api/, a WP_Error returned during normalization is now propagated by the REST controller. It defaults to HTTPHTTP HTTP is an acronym for Hyper Text Transfer Protocol. HTTP is the underlying protocol used by the World Wide Web and this protocol defines how messages are formatted and transmitted, and what actions Web servers and browsers should take in response to various commands. status 400 unless the error specifies another status, such as 422 or 429.

Filtering permission results with wp_ability_permission_result

The wp_ability_permission_result filter runs inside WP_Ability::check_permissions(), after the registered permission_callback has executed.

/**
 * Filters the result of an ability permission check.
 *
 * @param bool|WP_Error $permission   Result from permission_callback.
 * @param string        $ability_name Name of the ability.
 * @param mixed         $input        Input used for the permission check.
 * @param WP_Ability    $ability      Ability instance.
 */
apply_filters(
    'wp_ability_permission_result',
    $permission,
    $ability_name,
    $input,
    $ability
);

The filter may return:

  • true to permit execution.
  • false to deny execution.
  • A WP_Error to deny execution with a specific error and message.

Any other return value is converted to false.

Because the filter is part of check_permissions(), it also applies when permission checks are performed independently of execute(), including REST API and WP-CLIWP-CLI WP-CLI is the Command Line Interface for WordPress, used to do administrative and development tasks in a programmatic way. The project page is http://wp-cli.org/ https://make.wordpress.org/cli/ integrations.

Applying an additional authorisation policy

add_filter(
    'wp_ability_permission_result',
    function ( $permission, $ability_name, $input, $ability ) {
        if ( 'my-plugin/delete-records' !== $ability_name ) {
            return $permission;
        }

        // preserve both false and WP_Error results
        if ( false === $permission || is_wp_error( $permission ) ) {
            return $permission;
        }

        if ( ! current_user_can( 'manage_options' ) ) {
            return new WP_Error(
                'ability_additional_permission_required',
                __( 'This operation requires administrator access.', 'my-plugin' )
            );
        }

        return true;
    },
    10,
    4
);

Plugins should exercise particular care with this filter because returning true can override a denial from the ability’s original permission_callback.

Transforming results with wp_ability_execute_result

The wp_ability_execute_result filter runs after the ability’s registered execution callback and before output validation.

/**
 * Filters the result returned by an ability execute callback.
 *
 * @param mixed      $result       Result returned by the execute callback,
 *                                 or WP_Error when execution failed.
 * @param string     $ability_name Name of the ability.
 * @param mixed      $input        Normalized input.
 * @param WP_Ability $ability      Ability instance.
 */
apply_filters(
    'wp_ability_execute_result',
    $result,
    $ability_name,
    $input,
    $ability
);

Possible uses include:

  • Formatting a response.
  • Removing internal metadata.
  • Applying content-safety filtering.
  • Enriching a result.
  • Converting a successful result into an error.
  • Recovering from an execution error.

Removing internal response data

add_filter(
    'wp_ability_execute_result',
    function ( $result, $ability_name, $input, $ability ) {
        if (
            'my-plugin/get-report' !== $ability_name ||
            is_wp_error( $result ) ||
            ! is_array( $result )
        ) {
            return $result;
        }

        unset( $result['internal_debug_data'] );

        return $result;
    },
    10,
    4
);

The filtered result is subsequently validated against the ability’s output_schema.

Recovering from selected execution failures

The filter receives WP_Error values produced by the registered callback, so plugins may implement narrowly scoped recovery behaviour:

add_filter(
    'wp_ability_execute_result',
    function ( $result, $ability_name, $input, $ability ) {
        if (
            'my-plugin/get-remote-data' !== $ability_name ||
            ! is_wp_error( $result ) ||
            'remote_service_unavailable' !== $result->get_error_code()
        ) {
            return $result;
        }

        $fallback = my_plugin_get_fallback_data();

        /*
         * The fallback must conform to the ability's registered
         * output_schema because it will be validated after this filter.
         */
        return $fallback;
    },
    10,
    4
);

Any recovered value must still conform to the registered output_schema.

New WP_Filter_Sentinel class

WordPress 7.1 also introduces WP_Filter_Sentinel, a reusable marker class loaded alongside WP_Hook.

CoreCore Core is the set of software required to run WordPress. The Core Development Team builds WordPress. uses a unique sentinel instance as the default value for wp_pre_execute_ability. Comparing object identity allows Core to distinguish an unchanged default from every possible user-supplied value, including null, false, arrays and arbitrary objects.

Developers using wp_pre_execute_ability do not need to instantiate this class. To continue normal execution, callbacks should simply return the received $pre value unchanged.

Backward compatibility

These changes are additive:

  • Existing abilities require no changes.
  • Existing wp_before_execute_ability and wp_after_execute_ability callbacks continue to work.
  • Ability callbacks and permission callbacks retain their existing behaviour when none of the new filters is used.
  • Input and output schemas remain the final validation boundaries for normal execution.

Plugins that already provide their own ability-execution hooksHooks In WordPress theme and development, hooks are functions that can be applied to an action or a Filter in WordPress. Actions are functions performed when a certain event occurs in WordPress. Filters allow you to modify certain functions. Arguments used to hook both filters and actions look the same. may wish to evaluate whether some functionality can now use these Core filters. Protocol- or domain-specific hooks can still be layered on top when they need more specialised context.

Summary

WordPress 7.1 adds the following Abilities API filters:

FilterPurpose
wp_pre_execute_abilityShort-circuit the complete execution pipeline
wp_ability_normalize_inputTransform normalized input before validation
wp_ability_permission_resultModify or override permission results
wp_ability_execute_resultTransform or recover results before output validation

These filters make the Abilities API more extensibleExtensible This is the ability to add additional functionality to the code. Plugins extend the WordPress core software. for AI integrations, automation systems, protocol adapters, authorisation layers and other tools that mediate ability execution.

For additional context, see Trac ticket #64989 and changeset [62397].

Props to @benjamin_zekavica and @audrasjb for peer review.

#abilities-api, #7-1, #dev-notes, #dev-notes-7-1

X-post: Help shape the next chapter of WP Credits: a developer track, and a pilot opportunity

X-comment from +make.wordpress.org/community: Comment on Help shape the next chapter of WP Credits: a developer track, and a pilot opportunity

Performance Chat Summary: 28 July 2026

The full chat log is available beginning here on Slack.

WordPress Performance TracTrac An open source project by Edgewall Software that serves as a bug tracker and project management tool for WordPress. tickets

  • @westonruter highlighted #65634, a minor improvement to the development environment to make it easier to do performance tests so it is more reflective of an actual normal environment, with a small patchpatch A special text file that describes changes to code, by identifying the files and lines which are added, removed, and altered. It may also be referred to as a diff. A patch can be applied to a codebase for testing. ready for review.
  • For #65215, @westonruter shared that there is some feedback on the PR #11790 that has not been actioned yet.

    Performance Lab PluginPlugin A plugin is a piece of software containing a group of functions that can be added to a WordPress website. They can extend functionality or add new features to your WordPress websites. WordPress plugins are written in the PHP programming language and integrate seamlessly with WordPress. These can be free in the WordPress.org Plugin Directory https://wordpress.org/plugins/ or can be cost-based plugin from a third-party. (and other performance plugins)

    • @westonruter highlighted PR #2601, which updates wp-coding-standards/wpcs to 3.4.1, and shared that there was an important security release for WPCSWordPress Community Support A public benefit corporation and a subsidiary of the WordPress Foundation, established in 2016.. While it has been applied to the Performance repo, anyone with other repositories using an older version should update as soon as possible.
    • @mukesh27 shared that work has been ongoing over the past few weeks on accurate sizes for the Gallery blockBlock Block is the abstract term used to describe units of markup that, composed together, form the content or layout of a webpage using the WordPress editor. The idea combines concepts of what in the past may have achieved with shortcodes, custom HTML, and embed discovery into a single consistent API and user experience. and that the full PR will soon be ready for review.
    • @b1ink0 asked for feedback on a comment in PR #2336. @westonruter shared that it has been added to saved items.

    Our next chat will be held on Tuesday, August 11, 2026 at 16:00 UTC in the #core-performance channel in Slack.

    #core-performance, #hosting, #performance, #performance-chat, #summary

    Dev Chat Agenda – July 28, 2026

    The next WordPress Developers Chat will take place on Tuesday, July 28, 2026, at 15:00 UTC in the core channel on Make WordPress Slack.

    The live meeting will focus on the discussion for upcoming releases, and have an open floor section.

    The various curated agenda sections below refer to additional items. If you have ticketticket Created for both bug reports and feature development on the bug tracker. requests for help, please continue to post details in the comments section at the end of this agenda or bring them up during the dev chat.

    Announcements 📢

    Note: Dev Chat has been moved to Tuesdays at 15:00 UTC for the duration of the 7.1 release cycle.

    7.1

    General

    Discussions 💬

    The discussion section of the agenda is for discussing important topics affecting the upcoming release or larger initiatives that impact the CoreCore Core is the set of software required to run WordPress. The Core Development Team builds WordPress. Team. To nominate a topic for discussion, please leave a comment on this agenda with a summary of the topic, any relevant links that will help people get context for the discussion, and what kind of feedback you are looking for from others participating in the discussion.

    Open floor  🎙️

    Any topic can be raised for discussion in the comments, as well as requests for assistance on tickets. Tickets in the milestone for the next major or maintenance release will be prioritized.

    Please include details of tickets / PRs and the links in the comments, and indicate whether you intend to be available during the meeting for discussion or will be async.

    #7-1, #agenda, #core, #dev-chat

    New Block Support in WordPress 7.1: Background Gradient (background.gradient)

    WordPress 7.1 introduces a new background.gradient blockBlock Block is the abstract term used to describe units of markup that, composed together, form the content or layout of a webpage using the WordPress editor. The idea combines concepts of what in the past may have achieved with shortcodes, custom HTML, and embed discovery into a single consistent API and user experience. support. It gives blocks a gradient control in the Background panel of the block inspector. Unlike the existing gradient support, it can be combined with a background image so the two render together.

    GitHubGitHub GitHub is a website that offers online implementation of git repositories that can easily be shared, copied and modified by other developers. Public repositories are free to host, private repositories require a paid subscription. GitHub introduced the concept of the ‘pull request’ where code changes done in branches by contributors can be reviewed and discussed before being merged by the repository owner. https://github.com/ issue: #32787 | PR: #75859

    Background

    Until now, the only way to apply a gradient to a block was through the Color panel’s color.gradient support. That value is stored at style.color.gradient and rendered as a background CSSCSS Cascading Style Sheets. shorthand.

    The background shorthand resets every background property, including background-image. This meant a gradient set through color.gradient would conflictconflict A conflict occurs when a patch changes code that was modified after the patch was created. These patches are considered stale, and will require a refresh of the changes before it can be applied, or the conflicts will need to be resolved. with, and override, any background image on the same block. A block could show a gradient or an image, but not both.

    What changed

    A new background.gradient block support is registered. It stores its value at style.background.gradient, separate from the existing style.color.gradient.

    The key difference is that the new support renders through the background-image longhand property instead of the background shorthand. Because it avoids the shorthand, it no longer resets the other background properties. The style engine can then output the gradient and any background image as comma-separated values in a single background-image declaration:

    background-image: linear-gradient( 135deg, #000 0%, #fff 100% ), url( 'https://example.com/image.jpg' );

    The gradient is layered over the image, and both render together.

    Key behavior:

    • The control appears in the Background panel, next to the existing background image control.
    • When background.gradient is enabled for a block, the gradient tab in the Color panel is suppressed to avoid duplicate controls.
    • Gradient preset slugs resolve to CSS custom properties, for example var( --wp--preset--gradient--vivid-cyan-blue ).
    • When only a gradient is set (no image), it renders on its own as the background-image value.

    How to opt in

    Add gradient under the background support in block.json:

    {
    	"supports": {
    		"background": {
    			"backgroundImage": true,
    			"gradient": true,
    			"__experimentalDefaultControls": {
    				"backgroundImage": true,
    				"gradient": true
    			}
    		}
    	}
    }

    In WordPress 7.1, the Group, Accordion, Pullquote, Post Content, and Quote blocks opt in to the new support.

    In theme.json, the gradient can be set through the background styles group, at the root or per block:

    {
    	"styles": {
    		"background": {
    			"gradient": "linear-gradient( 135deg, #000 0%, #fff 100% )"
    		},
    		"blocks": {
    			"core/group": {
    				"background": {
    					"gradient": "var:preset|gradient|vivid-cyan-blue"
    				}
    			}
    		}
    	}
    }

    Block-level values set in the editor override theme defaults, following the same cascade as other block supports.

    How it works

    Frontend output. Server-side rendering in the background block support reads the gradient value and passes it, together with any background image, to the style engine. The engine merges them into one comma-separated background-image value and injects the result as an inline style on the block wrapper. Serialization for the image and the gradient is checked independently, so a block can skip one while still rendering the other.

    Sanitization. Previously, safecss_filter_attr() stripped a background-image value that mixed a gradient function with a url(). In WordPress 7.1, safecss_filter_attr() is updated to allow these combined gradient + url() values, so no additional filterFilter Filters are one of the two types of Hooks https://codex.wordpress.org/Plugin_API/Hooks. They provide a way for functions to modify data of other functions. They are the counterpart to Actions. Unlike Actions, filters are meant to work in an isolated manner, and should never have side effects such as affecting global variables and output. is required.

    Relationship to color.gradient

    background.gradient is a separate support from color.gradient. Existing blocks that use color.gradient are unchanged and continue to work exactly as before.

    This new support also lays the groundwork for eventually migrating gradient handling from color.gradient to background.gradient across blocks, giving a single, more capable background styling system. That migrationMigration Moving the code, database and media files for a website site from one server to another. Most typically done when changing hosting companies. is not part of this change.

    Backwards compatibility

    These are additive changes. No existing blocks are broken, and no action is required for most blocks and themes. Blocks that do not opt in to background.gradient behave exactly as they did before, including any current use of color.gradient.

    Summary

    ItemValue
    block.json support keysupports.background.gradient
    Style storage pathstyle.background.gradient
    theme.json pathstyles.background.gradient (and per block)
    Rendered CSS propertybackground-image (comma-separated with any image)
    CoreCore Core is the set of software required to run WordPress. The Core Development Team builds WordPress. adopters (7.1)core/group, core/accordion, core/pullquote, core/post-content, core/quote

    Further Reading

    #7-1, #blocks, #dev-notes, #dev-notes-7-1

    New Block Support in WordPress 7.1: Minimum Width

    WordPress 7.1 introduces a minWidth dimension blockBlock Block is the abstract term used to describe units of markup that, composed together, form the content or layout of a webpage using the WordPress editor. The idea combines concepts of what in the past may have achieved with shortcodes, custom HTML, and embed discovery into a single consistent API and user experience. support, letting blocks opt in to setting a minimum width. This follows the same pattern as the existing minHeight support.

    GitHubGitHub GitHub is a website that offers online implementation of git repositories that can easily be shared, copied and modified by other developers. Public repositories are free to host, private repositories require a paid subscription. GitHub introduced the concept of the ‘pull request’ where code changes done in branches by contributors can be reviewed and discussed before being merged by the repository owner. https://github.com/ issue: #76525 | PR: #76949

    Background

    Blocks already support several dimension properties: height, minHeight, and width. There was no matching control for a minimum width. Setting a floor on a block’s width is a common CSSCSS Cascading Style Sheets. need. For example, you may want a container or layout element to stay usable and not collapse below a certain size on smaller viewports.

    Until now, achieving this required custom CSS or a custom block style, since the design tools did not expose a min-width option. This release handles it natively through a new block support.

    What changed

    A new minWidth feature is registered under the existing dimensions block support. When a block opts in, a “Minimum width” control appears in the Dimensions panel, alongside the existing minimum height control. The value is applied as the CSS min-width property, and it supports dimension presets (dimensionSizes) where a theme provides them.

    How to use it

    A block opts in through its block.json supports, the same way it opts in to minHeight:

    {
    	"supports": {
    		"dimensions": {
    			"minWidth": true
    		}
    	}
    }

    To enable the setting for all blocks that support it, use theme.json. You can turn it on directly, or through Appearance Tools:

    {
    	"settings": {
    		"dimensions": {
    			"minWidth": true
    		}
    	}
    }

    You can also set a value in theme.json, either globally or per block:

    {
    	"styles": {
    		"dimensions": {
    			"minWidth": "400px"
    		},
    		"blocks": {
    			"core/group": {
    				"dimensions": {
    					"minWidth": "400px"
    				}
    			}
    		}
    	}
    }

    If the active theme defines dimensionSizes presets, the control offers those presets, and selecting one applies the matching --wp--preset--dimension--{slug} custom property.

    Where the control appears

    The minimum width control follows the same visibility rules as other optional design tools:

    • In the block inspector, the control is not shown by default. A block must opt in through __experimentalDefaultControls to show it automatically. Otherwise, you reveal it from the panel’s options menu (the three-dots menu on the Dimensions panel headerHeader The header of your site is typically the first thing people will experience. The masthead or header art located across the top of your page is part of the look and feel of your website. It can influence a visitor’s opinion about your content and you/ your organization’s brand. It may also look different on different screen sizes.).
    • In Global Styles (Site Editor), the control is shown by default.

    Block-level values set in the editor override the theme defaults, following the same cascade as other block supports.

    Backwards compatibility

    These are additive changes. No existing blocks are broken, and no action is required for most blocks and themes. Blocks that do not opt in behave exactly as before. Themes that do not enable the setting see no change.

    Summary

    LayerKey
    block.json supportsupports.dimensions.minWidth
    theme.json settingsettings.dimensions.minWidth
    theme.json stylestyles.dimensions.minWidth
    CSS propertymin-width
    Preset sourcedimensionSizes (--wp--preset--dimension--{slug})

    Further Reading

    #7-1, #blocks, #dev-notes, #dev-notes-7-1

    Registering and rendering SVG icons in WordPress 7.1

    WordPress 7.0 added a built-in set of SVG icons that the blockBlock Block is the abstract term used to describe units of markup that, composed together, form the content or layout of a webpage using the WordPress editor. The idea combines concepts of what in the past may have achieved with shortcodes, custom HTML, and embed discovery into a single consistent API and user experience. editor and the coreCore Core is the set of software required to run WordPress. The Core Development Team builds WordPress./icon block can use. In 7.1, this becomes a proper, public APIAPI An API or Application Programming Interface is a software intermediary that allows programs to interact with each other and share data in limited, clearly defined ways.: you can now add your own icons, group them, render them on the server, and read them over the REST APIREST API The REST API is an acronym for the RESTful Application Program Interface (API) that uses HTTP requests to GET, PUT, POST and DELETE data. It is how the front end of an application (think “phone app” or “website”) can communicate with the data store (think “database” or “file system”) https://developer.wordpress.org/rest-api/.

    Icons are registered in one place and can then be used in several: the editor, the REST API, and your own PHPPHP The web scripting language in which WordPress is primarily architected. WordPress requires PHP 7.4 or higher. This note covers the pieces you’ll work with:

    • Creating and removing icon collections.
    • Adding and removing individual icons.
    • Browsing icons by collection in the Icon block’s picker.
    • Rendering an icon in PHP with wp_get_icon().
    • The REST API endpoints for collections and icons.

    Icon collections

    Every icon belongs to a collection. A collection is just a named group of icons, and its name becomes a prefix: that’s what makes core/plus different from my-plugin/plus. This lets icons from different sources — WordPress, a pluginPlugin A plugin is a piece of software containing a group of functions that can be added to a WordPress website. They can extend functionality or add new features to your WordPress websites. WordPress plugins are written in the PHP programming language and integrate seamlessly with WordPress. These can be free in the WordPress.org Plugin Directory https://wordpress.org/plugins/ or can be cost-based plugin from a third-party., or a third-party icon set — live side by side without clashing.

    WordPress registers one collection by default, called core, with its own bundled icons.

    Creating a collection

    An icon can only be added to a collection that already exists, so start by registering the collection with wp_register_icon_collection().

    function my_plugin_register_icon_collection() {
    	wp_register_icon_collection(
    		'my-plugin',
    		array(
    			'label'       => __( 'My Plugin Icons', 'my-plugin' ),
    			'description' => __( 'Icons provided by My Plugin.', 'my-plugin' ),
    		)
    	);
    }
    add_action( 'init', 'my_plugin_register_icon_collection' );

    The first argument is the collection name. It must start and end with a lowercase letter or digit, and in between may contain lowercase letters, digits, hyphens, and underscores. The second is an array with a required label and an optional description.

    Removing a collection

    Use wp_unregister_icon_collection() with the collection name. Removing a collection also removes every icon in it, so you don’t need to remove the icons one by one. Run this on the init hook, at a later priority than the registration so the collection already exists:

    function my_plugin_unregister_icon_collection() {
    	wp_unregister_icon_collection( 'my-plugin' );
    }
    add_action( 'init', 'my_plugin_unregister_icon_collection', 20 );

    Icons

    Every icon name has the form collection/icon-name, for example my-plugin/star. The collection must already be registered when you register the icon.

    The icon-name part follows the same rule as a collection name: it must start and end with a lowercase letter or digit, and in between may contain lowercase letters, digits, hyphens, and underscores.

    Adding icons

    Register an icon with wp_register_icon(). You give it a label and the SVG itself — either inline as a string (content) or as an absolute path to an .svg file (file_path). Use one or the other, not both.

    Like the other registration functions, wp_register_icon() returns true on success and false on failure, emitting a _doing_it_wrong() notice that explains why. Registration fails for an invalidinvalid A resolution on the bug tracker (and generally common in software development, sometimes also notabug) that indicates the ticket is not a bug, is a support request, or is generally invalid. name, a name that isn’t namespaced as collection/icon-name, a collection that isn’t registered, a duplicate icon, a missing label, unsupported argument keys, or providing neither content nor file_path (or both).

    The SVG is sanitized through wp_kses against a small allowlist: only the <svg>, <path>, and <polygon> elements survive, each limited to a fixed set of attributes. Anything outside that subset — other elements, inline styles, scripts, or event handlers — is stripped. This allowlist is intentionally conservative and may be broadened in the future to cover more shape elements and attributes; see https://github.com/WordPress/gutenberg/pull/75550 for the ongoing work.

    Note that file_path is read lazily: the file isn’t opened at registration, only when the icon’s content is first needed, during REST retrieval or rendering. So registration can succeed even if the path is wrong; a missing or unreadable file surfaces later as empty content, not as a registration error. Make sure the path resolves on the environment where the icon is used.

    Icons can go into any registered collection. Most of the time, registering them under your own collection keeps them clearly separated from core’s.

    function my_plugin_register_icons() {
    	// Register a custom collection first, then add icons to it.
    	wp_register_icon_collection(
    		'my-plugin',
    		array(
    			'label' => __( 'My Plugin Icons', 'my-plugin' ),
    		)
    	);
    
    	// An icon from an inline SVG string.
    	wp_register_icon(
    		'my-plugin/star',
    		array(
    			'label'   => __( 'Star', 'my-plugin' ),
    			'content' => '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 2l2.9 6.9 7.1.6-5.4 4.7 1.6 7L12 18l-6.2 3.2 1.6-7L2 9.5l7.1-.6z" /></svg>',
    		)
    	);
    
    	// An icon from an .svg file shipped with the plugin.
    	wp_register_icon(
    		'my-plugin/heart',
    		array(
    			'label'     => __( 'Heart', 'my-plugin' ),
    			'file_path' => plugin_dir_path( __FILE__ ) . 'icons/heart.svg',
    		)
    	);
    }
    add_action( 'init', 'my_plugin_register_icons' );

    Removing an icon

    Remove a single icon by name with wp_unregister_icon(), again on the init hook after the icon has been registered. Like the registration functions, it returns true on success and false on failure, emitting a _doing_it_wrong() notice; the only failure case is that the icon isn’t registered.

    function my_plugin_unregister_icon() {
    	wp_unregister_icon( 'my-plugin/star' );
    }
    add_action( 'init', 'my_plugin_unregister_icon', 20 );

    To remove every icon in a collection at once, remove the collection instead (see Removing a collection).

    Icon block enhancementenhancement Enhancements are simple improvements to WordPress, such as the addition of a hook, a new feature, or an improvement to an existing feature.

    The icon picker in the core Icon block now groups icons by collection, so custom icons from plugins and themes appear alongside the core ones.

    Each collection has its own tab, plus an “All” tab covering every collection at once. Search filters the selected collection; use the All tab to search across collections. The search query is preserved when switching tabs.

    Icon picker

    The block itself picked up a few more changes:

    • Flip and rotate. The toolbar now has controls to flip the icon horizontally or vertically, and a button that rotates it 90 degrees at a time.
    • Default icon. A newly inserted Icon block now starts with core/info instead of an empty placeholder.
    • Server rendering via wp_get_icon(). The block’s server-side render now delegates to wp_get_icon() to produce the SVG markup, so a block-rendered icon and one you print yourself with wp_get_icon() go through the same code path.

    Rendering an icon in PHP

    Use wp_get_icon() to get the SVG markup for any registered icon, ready to print:

    // A decorative icon at the default 24px size.
    echo wp_get_icon( 'core/plus' );
    
    // A 32px icon with an accessible label and an extra CSS class.
    echo wp_get_icon(
    	'my-plugin/star',
    	array(
    		'size'  => 32,
    		'label' => __( 'Featured', 'my-plugin' ),
    		'class' => 'my-plugin-star',
    	)
    );

    The first argument is the icon name. If it isn’t registered, you get an empty string. The optional second argument accepts:

    • size — Width and height in pixels. Defaults to 24. Pass null to keep the SVG’s own size.
    • class — Extra CSSCSS Cascading Style Sheets. class names for the <svg> element.
    • label — An accessible label. If you provide one, the icon is announced to screen readers; if you leave it out, the icon is treated as decorative and hidden from them.

    Styling icons

    Styling ReactReact React is a JavaScript library that makes it easy to reason about, construct, and maintain stateless and stateful user interfaces. https://reactjs.org icons from @wordpress/icons

    Since version 15.0.0, each icon declares fill="currentColor" on its outer <svg>, so a React-rendered icon follows the current text color out of the box. By default the icon inherits color from its ancestors. To apply a different color, set color rather than fill — pass style={ { color } } to the icon:

    import { Icon, plus } from '@wordpress/icons';
    
    <Icon icon={ plus } style={ { color: '#3858e9' } } />;

    Styling the SVG returned by wp_get_icon()

    This markup is sanitized on registration, and the allowlist keeps fill only on the <path> and <polygon> shapes — not on the outer <svg> — and doesn’t permit stroke anywhere, so a stroke-based icon loses its stroke and you should stick to fill-based shapes for now. (This is a current limitation: the allowlist may be relaxed in the future to cover stroke and other attributes; see https://github.com/WordPress/gutenberg/pull/75550 for the ongoing work.) One upshot is that the fill="currentColor" the React icons carry on their <svg> does not survive here, and wp_get_icon() doesn’t add one. That has two consequences:

    • Inside the Icon block, coloring still works, because the block’s stylesheet sets fill: currentColor on .wp-block-icon svg. A block-rendered icon therefore follows the text color.
    • A standalone wp_get_icon() call returns bare markup with no such rule, so by default it renders in the SVG’s own fill (black), not the surrounding text color.

    To make a standalone icon follow the text color, you have two options.

    Supply your own CSS. Render the icon with a class (wp_get_icon() puts it on the <svg>), then set fill on it — since fill is inherited, it cascades to the shapes:

    echo wp_get_icon( 'my-plugin/star', array( 'class' => 'my-icon' ) );
    .my-icon {
    	fill: currentColor;
    }

    Alternatively, put fill="currentColor" on the shape when you register the icon. The allowlist keeps fill on <path> and <polygon>, so it survives sanitization and the icon carries its own color behavior wherever it’s rendered:

    wp_register_icon(
    	'my-plugin/star',
    	array(
    		'label'   => __( 'Star', 'my-plugin' ),
    		'content' => '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill="currentColor" d="M12 2l2.9 6.9 7.1.6-5.4 4.7 1.6 7L12 18l-6.2 3.2 1.6-7L2 9.5l7.1-.6z" /></svg>',
    	)
    );

    REST API

    The editor reads icons and collections over the REST API, and your own code can too. These endpoints are read-only: every route is a GET, so you can browse registered icons and collections but can’t register or change them over REST. All endpoints are under wp/v2 and require an authenticated user who can edit_posts, or who holds the equivalent edit capabilitycapability capability is permission to perform one or more types of task. Checking if a user has a capability is performed by the current_user_can function. Each user of a WordPress site might have some permissions but not others, depending on their role. For example, users who have the Author role usually have permission to edit their own posts (the “edit_posts” capability), but not permission to edit other users’ posts (the “edit_others_posts” capability). for any REST-visible (show_in_rest) post type.

    Collections:

    • GET /wp/v2/icon-collections — All collections.
    • GET /wp/v2/icon-collections/<collection> — A single collection.

    Each collection is returned as an object with slug, label, and description. For example, GET /wp/v2/icon-collections/core returns:

    {
    	"slug": "core",
    	"label": "WordPress",
    	"description": "Default icon collection."
    }

    Icons:

    • GET /wp/v2/icons — All icons. (introduced in 7.0 with the name, label, and content fields and the search parameter; 7.1 adds the collection field and parameter)
    • GET /wp/v2/icons/<collection> — Icons in one collection. (new in 7.1)
    • GET /wp/v2/icons/<collection>/<name> — A single icon. (introduced in 7.0)

    Each icon is returned as an object with name (the full collection/icon-name), label, content (the sanitized SVG markup), and collection (the slug of the collection it belongs to). For example, GET /wp/v2/icons/core/plus returns:

    {
    	"name": "core/plus",
    	"label": "Plus",
    	"content": "<svg xmlns=\"http://www.w3.org/2000/svg\" viewbox=\"0 0 24 24\"><path d=\"M11 12.5V17.5H12.5V12.5H17.5V11H12.5V6H11V11H6V12.5H11Z\" /></svg>",
    	"collection": "core"
    }

    The icon list also accepts two query parameters: search to filterFilter Filters are one of the two types of Hooks https://codex.wordpress.org/Plugin_API/Hooks. They provide a way for functions to modify data of other functions. They are the counterpart to Actions. Unlike Actions, filters are meant to work in an isolated manner, and should never have side effects such as affecting global variables and output. by name or label, and collection to limit results to one collection. For example:

    GET /wp/v2/icons?collection=my-plugin&search=star

    Props to @tyxla for review.

    #7-1, #dev-notes, #dev-notes-7-1

    React 19: punted beyond WordPress 7.1, experiment in Gutenberg

    WordPress 7.1 continues to use ReactReact React is a JavaScript library that makes it easy to reason about, construct, and maintain stateless and stateful user interfaces. https://reactjs.org 18.3

    React 19 upgrade won’t be a part of WordPress 7.1. After briefly enabling it in GutenbergGutenberg The Gutenberg project is the new Editor Interface for WordPress. The editor improves the process and experience of creating new content, making writing rich content much simpler. It uses ‘blocks’ to add richness rather than shortcodes, custom HTML etc. https://wordpress.org/gutenberg/ we discovered unexpected incompatibilities in how old and new version of React interact with each other, and in the ways how plugins use React, and we were forced to revert the change. We’ll need a considerable testing period where we improve and fine-tune the compatibility layer that allows the existing plugins to run seamlessly.

    Experimental flag in Gutenberg

    Instead, there is a new experiment in the Gutenberg pluginPlugin A plugin is a piece of software containing a group of functions that can be added to a WordPress website. They can extend functionality or add new features to your WordPress websites. WordPress plugins are written in the PHP programming language and integrate seamlessly with WordPress. These can be free in the WordPress.org Plugin Directory https://wordpress.org/plugins/ or can be cost-based plugin from a third-party. (since version 23.4) that enables React 19 on your WordPress site, intended for testing plugin compatibility. To enable the experiment, install the Gutenberg plugin and check a checkbox on the Gutenberg Experiments page (under Settings › Gutenberg):

    Testing plugins

    Testing a plugin compatibility generally means trying to use all parts of the plugin that uses React, typically Gutenberg blocks and extensions, and also custom WP Adminadmin (and super admin) pages, and verifying that the UIUI User interface is not broken and there are no errors logged in the browser console.

    What kind of errors to look for

    Typical failure modes for plugins are:

    1. Bundling the react/jsx-runtime code directly in the plugin JavaScriptJavaScript JavaScript or JS is an object-oriented computer programming language commonly used to create interactive effects within web browsers. WordPress makes extensive use of JS for a better user experience. While PHP is executed on the server, JS executes within a user’s browser. https://www.javascript.com code instead of using the “externalized” react-jsx-runtime script provided by WordPress itself. Such bundling leads to a mixture of React 18 (bundled) and React 19 (provided by WordPress) running together and passing data structures created by the old version to the new runtime. If everyone was bundling their JavaScript correctly, the majority of compat issues wouldn’t happen at all.
    2. Using really old React features that were removed in React 19, after being deprecated for a long time (at least 6 years). String refs, default props on function components, legacy ways of defining context, … Some of them we’re polyfilling in the compat layer. A more detailed overview can be found in the “Removed APIs” section of an earlier post about the React 19 upgrade.

    There is also work in progress to automatically detect these issues as part of the Plugin Check plugin.

    Everyone is invited to test and update their plugins, and report issues in the Gutenberg GitHubGitHub GitHub is a website that offers online implementation of git repositories that can easily be shared, copied and modified by other developers. Public repositories are free to host, private repositories require a paid subscription. GitHub introduced the concept of the ‘pull request’ where code changes done in branches by contributors can be reviewed and discussed before being merged by the repository owner. https://github.com/ repo.


    Props to @tyxla, @aduth, @simison, @wildworks and @mamaduka for contributions, feedback and code reviews.

    Props to @tyxla and @aduth for reviewing a draft of this post.

    #7-1, #dev-notes, #dev-notes-7-1