Skip to content

Command Palette: Make every admin screen faster by loading the palette only when it is opened - #15871

Open
rene-hermenau wants to merge 2 commits into
WordPress:trunkfrom
rene-hermenau:66642-command-palette-on-demand
Open

rene-hermenau wants to merge 2 commits into
WordPress:trunkfrom
rene-hermenau:66642-command-palette-on-demand

Conversation

@rene-hermenau

@rene-hermenau rene-hermenau commented Nov 10, 2026 •

Copy link
Copy Markdown

This makes wp-admin pages ready about twice as fast for everyone who works in the dashboard, by loading the Command Palette only when someone actually opens it instead of on every page.

On a Settings screen, the page becomes usable in 57 ms instead of 127 ms with a warm cache, and in 299 ms instead of 910 ms on a simulated slower device, with less than half the scripts. The palette works exactly as before: Ctrl/Cmd+K and the toolbar button still open it, and the editors are unchanged.

Why

Since 6.9, every admin screen enqueues wp-commands and wp-core-commands, which pull in about 40 block-editor scripts (React, wp-components, wp-block-editor, wp-core-data, …) and the wp-components stylesheet, although the palette is only needed after Ctrl/Cmd+K or a click on its toolbar button.

How

  • wp_enqueue_command_palette_loader() replaces wp_enqueue_command_palette_assets() on admin_enqueue_scripts. It builds the same settings and menu commands, attaches the initializeCommandPalette() call to wp-core-commands, and enqueues only the new 1.4 KB wp-command-palette-loader script.
  • wp_print_command_palette_assets_template() prints the palette's not-yet-printed styles and scripts, with their inline scripts and translations, into an inert <template> in the footer, using WP_Styles::do_items() / WP_Scripts::do_items().
  • On the shortcut or a toolbar click, the loader preloads the template's scripts, inserts them in order, inserts the styles, and opens the palette. After that the palette's own shortcut handler takes over.
  • Screens that already load wp-commands (post editor, site editor) keep loading the palette up front. wp_enqueue_command_palette_assets() still loads everything up front for direct callers such as the Font Library and Connectors pages.
  • A new enqueue_command_palette_assets action lets plugins add their commands without checking wp_script_is( 'wp-commands', 'enqueued' ), which is now false on classic screens. It fires right away where the palette loads up front, and in the footer where it loads on demand; what is enqueued on it goes into the template and loads together with the palette.
  • The toolbar button drops its inline onclick (which needs wp.data); the loader handles the click.
  • The login screen no longer prefetches the wp-commands stylesheet, since admin screens no longer load it up front.

Measured on the local env, wp-admin/options-general.php, SCRIPT_DEBUG off, headless Chromium, 8 rounds, median DOMContentLoaded:

trunk this PR
<script src> on the page 80 39
empty cache 251 ms 97 ms
warm cache 127 ms 57 ms
empty cache, 4× CPU slowdown 910 ms 299 ms
warm cache, 4× CPU slowdown 452 ms 135 ms

The first open of the palette on a page takes 200–260 ms with an empty cache and about 100 ms with a warm cache. Full numbers and method are in the ticket.

Tests: new tests/phpunit/tests/dependencies/wpCommandPaletteLoader.php, updated wpPrefetchAdminAssets.php, and a new Playwright spec tests/e2e/specs/command-palette.test.js.

Trac ticket: https://core.trac.wordpress.org/ticket/66642

Use of AI Tools

AI assistance: Yes
Tool(s): Claude Code
Model(s): Claude Opus 5.5
Used for: Implementation, tests, measurements and the ticket text, under my direction; I reviewed the change and take responsibility for it.


This Pull Request is for code review only. Please keep all other discussion in the Trac ticket. Do not merge this Pull Request. See GitHub Pull Requests for Code Review in the Core Handbook for more details.

Every admin screen enqueued `wp-commands` and `wp-core-commands`, which pull in about 40 block editor scripts and the `wp-components` stylesheet before `DOMContentLoaded`, although the palette is only needed once it is opened.

Admin screens now enqueue a small loader instead. The palette's scripts and styles are printed into an inert template in the footer and added to the page on the first Ctrl/Cmd+K or click on the toolbar button. Screens that already load the commands package, such as the block editor, keep loading the palette up front, as does `wp_enqueue_command_palette_assets()`.

On `options-general.php` this drops the scripts on the page from 80 to 39 and the median DOMContentLoaded with a warm cache from 127 ms to 57 ms.

See #66642.
@github-actions

github-actions Bot commented Nov 10, 2026 •

Copy link
Copy Markdown

The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs.

Core Committers: Use this line as a base for the props when committing in SVN:

Props renehermi, mindctrl.

To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook.


Props Bot commands

You can trigger Props Bot actions by commenting on this PR:

  • @propsbot add <username> to give props to a GitHub user. Requires write access.
  • @propsbot remove <username> to leave a user out of the props. Requires write access.
  • @propsbot refresh to refresh this list. Adding the props-bot label works too.

@github-actions

Copy link
Copy Markdown

Test using WordPress Playground

The changes in this pull request can previewed and tested using a WordPress Playground instance.

WordPress Playground is an experimental project that creates a full WordPress instance entirely within the browser.

Some things to be aware of

  • All changes will be lost when closing a tab with a Playground instance.
  • All changes will be lost when refreshing the page.
  • A fresh instance is created each time the link below is clicked.
  • Every time this pull request is updated, a new ZIP file containing all changes is created. If changes are not reflected in the Playground instance,
    it's possible that the most recent build failed, or has not completed. Check the list of workflow runs to be sure.

For more details about these limitations and more, check out the Limitations page in the WordPress Playground documentation.

Test this pull request with WordPress Playground.

@mindctrl

Copy link
Copy Markdown
Member

A side effect of this PR is that plugins like User Switching that register their commands behind checks like this will find their commands missing from the palette: wp_script_is( 'wp-commands', 'enqueued' ). See user_switching::action_enqueue_command_palette_script(). So if you're on a Settings page or somewhere like that, the command will be missing. I wonder if a new action inside wp_print_command_palette_assets_template() would help.

The PR also exposes an existing bug in the Fresh admin color scheme, where styles don't look correct until the command palette is invoked.

I'm not seeing this in Playground, but I see it locally using wordpress-develop with this PR.

To reproduce:

  • change your admin color scheme to Fresh
  • navigate to Settings > General (or just refresh the Users > Profile screen.
  • notice the radio buttons, checkboxes, and button colors are not displayed properly.
  • open the Command Palette and see all the above things be displayed as they should be

I asked Claude to debug this, and it found the following:

admin-schemes.css sets --wp-admin-theme-color on body.admin-color-* for every scheme except fresh. Under Fresh, the variable only comes from the :root blocks in the wp-components and wp-commands stylesheets, which this PR no longer loads up front. Meanwhile forms.css, common.css, edit.css and others use var(--wp-admin-theme-color) with no fallback.

On options-general.php with Fresh:

┌─────────────────────────────┬─────────────────────────────────────────────────┬──────────────────┐
│                             │           Before opening the palette            │ After opening it │
├─────────────────────────────┼─────────────────────────────────────────────────┼──────────────────┤
│ --wp-admin-theme-color      │ unset                                           │ #007cba          │
├─────────────────────────────┼─────────────────────────────────────────────────┼──────────────────┤
│ Checked checkbox background │ transparent (the white checkmark can't be seen) │ rgb(0,124,186)   │
├─────────────────────────────┼─────────────────────────────────────────────────┼──────────────────┤
│ .button-primary             │ #3858e9 (from its fallback)                     │ #007cba          │
└─────────────────────────────┴─────────────────────────────────────────────────┴──────────────────┘

So the checkboxes look broken, and the page's colors change when the palette opens. The default modern scheme is unaffected. The real cause is the missing Fresh rule in base-styles; this PR just exposes it. That should be fixed first or alongside, either with a Fresh block in @wordpress/base-styles or a :root default in an admin stylesheet that every screen loads.

Plugins that add commands to the palette, such as User Switching, enqueue their script only when `wp_script_is( 'wp-commands', 'enqueued' )` is true. Since the palette is now loaded on demand on classic screens, that check is false there and their commands were missing.

The new `enqueue_command_palette_assets` action fires wherever the palette's assets are enqueued: right away on screens that load the palette up front, and in the admin footer on screens that load it on demand, where whatever is enqueued on it is printed into the palette's template and loaded together with the palette.

`wp_enqueue_command_palette_assets()` now enqueues `wp-commands` and lets `wp_enqueue_command_palette_loader()` take the up-front path, so the action fires for its direct callers too.

See #66642.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants