Opened 4 hours ago
Last modified 4 minutes ago
#66642 new enhancement
Command Palette: Make every admin screen faster by loading the palette only when it is opened
| Reported by: | ReneHermi | Owned by: | |
|---|---|---|---|
| Priority: | normal | Milestone: | Awaiting Review |
| Component: | Script Loader | Version: | 6.9 |
| Severity: | normal | Keywords: | has-patch has-unit-tests |
| Cc: | Focuses: | javascript, performance |
Description
This patch 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 itself works exactly as before: Ctrl/Cmd+K and the toolbar button still open it, and the editors are unchanged.
Background
Since [61022] (#63845), wp_enqueue_command_palette_assets() runs on admin_enqueue_scripts for every admin screen. It enqueues wp-commands and wp-core-commands, which pull in most of the block editor's dependency tree: react, react-dom, wp-element, wp-components, wp-data, wp-core-data, wp-block-editor, wp-blocks, wp-rich-text, wp-compose, moment, wp-api-fetch, wp-router, wp-preferences, wp-keyboard-shortcuts and others, plus the render-blocking wp-commands and wp-components stylesheets.
Every classic admin screen (Dashboard, Posts, Plugins, Settings, any plugin's own page) downloads, parses and executes all of this before DOMContentLoaded, although the palette is only needed once someone presses Ctrl/Cmd+K or clicks its toolbar button (#64672).
Proposed change
The attached patch loads the palette on first use on screens that do not already load the commands package.
wp_enqueue_command_palette_loader()replaceswp_enqueue_command_palette_assets()onadmin_enqueue_scripts. It builds the palette settings (menu commands included) exactly as before, attaches thewp.coreCommands.initializeCommandPalette()call towp-core-commands, and enqueues only a new 1.4 KB script,wp-command-palette-loader.wp_print_command_palette_assets_template()runs onadmin_print_footer_scripts(priority 20). It prints thewp-commandsstyle and thewp-core-commandsscript withWP_Styles::do_items()/WP_Scripts::do_items()into an inert<template id="wp-command-palette-assets">. Only what the page has not printed yet ends up there, and inline scripts, translations and localized data come along, since the regular printing code produces the markup.- On Ctrl/Cmd+K or a click on the toolbar button, the loader preloads every script in the template, inserts them in their printed order (waiting for each one, so inline
before/afterscripts keep their order), inserts the stylesheets, and then opens the palette withwp.data.dispatch( 'core/commands' ).open(). After that the palette's own keyboard shortcut handler takes over. - Screens that already load the commands package, such as the post editor and the site editor, keep loading the palette up front: if
wp-commandsis already enqueued,wp_enqueue_command_palette_loader()enqueueswp-core-commandsand thewp-commandsstyle as before, and no template is printed. wp_enqueue_command_palette_assets()stays for direct callers such as the boot-based Font Library and Connectors pages, and still loads everything up front.- The toolbar button no longer has an inline
onclickthat callswp.data, which would not exist yet on a classic screen; the loader handles the click on every screen. wp_prefetch_admin_assets()no longer prefetches thewp-commandsstylesheet from the login screen, since admin screens no longer load it up front. The editor prefetch still covers it throughwp-edit-post.
I considered the smaller step of registering the palette handles with the defer strategy. It would stop them from blocking the parser, but deferred scripts still download and execute before DOMContentLoaded fires, on every screen. Loading on demand removes the cost from screens where the palette is never opened.
Measurements
Local wordpress-develop environment (trunk at 7.2-beta4-65231-src, Docker, PHP latest, nginx without gzip), single site, wp-admin/options-general.php, SCRIPT_DEBUG off. Headless Chromium (Playwright 1.64), 8 rounds each; each round is a fresh browser context (empty cache) followed by a reload (warm cache). Trunk was measured first, the patch afterwards on the same server. DOMContentLoaded is domContentLoadedEventEnd from Navigation Timing, given as median (min–max).
| trunk | patch | |
|---|---|---|
<script src> on the page | 80 | 39 |
| stylesheets on the page | 29 | 26 |
| bytes transferred, empty cache (uncompressed) | 7.35 MB | 1.57 MB |
| DOMContentLoaded, empty cache | 251 ms (236–902) | 97 ms (95–101) |
| DOMContentLoaded, warm cache | 127 ms (120–144) | 57 ms (55–108) |
| DOMContentLoaded, empty cache, 4× CPU slowdown | 910 ms (902–928) | 299 ms (292–303) |
| DOMContentLoaded, warm cache, 4× CPU slowdown | 452 ms (448–463) | 135 ms (128–141) |
The first round of the unthrottled trunk run (902 ms) is a cold-server outlier.
The cost moves to the first time the palette is opened on a page: from the key press to the visible palette took 202–261 ms with an empty cache and 97–102 ms with a warm cache (localhost, no throttling). Opening it again on the same page is instant, as before.
Earlier, on a production WordPress 7.1.4 site (single site, a plugin admin page, headless Chromium, 8 alternating rounds with and without remove_action( 'admin_enqueue_scripts', 'wp_enqueue_command_palette_assets' )), the palette accounted for 42 of 59 scripts, +0.57 s DOMContentLoaded with an empty cache (1.65–1.79 s vs. 1.09–1.30 s) and about 2× with a warm cache (0.31–0.36 s vs. 0.17–0.21 s).
Testing
- New PHPUnit tests in
tests/phpunit/tests/dependencies/wpCommandPaletteLoader.php;wpPrefetchAdminAssets.phpupdated. Thedependencies,scripts,admin-barandscript-modulesgroups pass on single site and multisite, and the full single-site suite passes (32,226 tests). - New Playwright spec
tests/e2e/specs/command-palette.test.js: the palette is not loaded up front on a classic screen, opens with the shortcut and the toolbar button, runs a menu command, opens again after loading, and is still loaded up front in the block editor. - Checked by hand: Cmd+K and the toolbar button on Settings and Posts, searching and running "Settings > Permalinks", the post editor and the site editor, no console errors.
Attachments (1)
Change History (4)
This ticket was mentioned in PR #15871 on WordPress/wordpress-develop by @ReneHermi.
4 hours ago
#1
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()replaceswp_enqueue_command_palette_assets()onadmin_enqueue_scripts. It builds the same settings and menu commands, attaches theinitializeCommandPalette()call towp-core-commands, and enqueues only the new 1.4 KBwp-command-palette-loaderscript.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, usingWP_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. - The toolbar button drops its inline
onclick(which needswp.data); the loader handles the click. - The login screen no longer prefetches the
wp-commandsstylesheet, 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.
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 ticket was mentioned in Slack in #core-performance by mashshare. View the logs.
4 hours ago
#3
@
4 minutes ago
Reviewed the approach. Using an inert template in the footer (wp_print_command_palette_assets_template) to queue unprinted scripts/styles and preloading them on first interaction (Ctrl/Cmd+K or the toolbar button) is clean and avoids breaking existing dependencies. Big win for admin performance.
![(please configure the [header_logo] section in trac.ini)](/chrome/site/your_project_logo.png)
Load the Command Palette on demand, with PHPUnit and e2e tests.