diff --git a/lib/experimental/commands.php b/lib/experimental/commands.php
new file mode 100644
index 00000000000000..f0de74cbd7e660
--- /dev/null
+++ b/lib/experimental/commands.php
@@ -0,0 +1,36 @@
+is_block_editor() ) {
+ return;
+ }
+
+ wp_enqueue_script( 'wp-commands' );
+ wp_enqueue_style( 'wp-commands' );
+ wp_enqueue_script( 'wp-core-commands' );
+
+ $inline_script = 'wp.coreCommands.initializeCommandPalette();';
+
+ wp_add_inline_script( 'wp-core-commands', $inline_script );
+}
+
+add_action( 'admin_enqueue_scripts', 'gutenberg_enqueue_command_palette_assets' );
diff --git a/lib/experiments-page.php b/lib/experiments-page.php
index a2032eb99abdc5..0daa526f77e7b3 100644
--- a/lib/experiments-page.php
+++ b/lib/experiments-page.php
@@ -91,6 +91,18 @@ function gutenberg_initialize_experiments_settings() {
)
);
+ add_settings_field(
+ 'gutenberg-command-palette-everywhere',
+ __( 'Command Palette: enable everywhere', 'gutenberg' ),
+ 'gutenberg_display_experiment_field',
+ 'gutenberg-experiments',
+ 'gutenberg_experiments_section',
+ array(
+ 'label' => __( 'Enables the Command Palette everywhere in the admin dashboard.', 'gutenberg' ),
+ 'id' => 'gutenberg-command-palette-everywhere',
+ )
+ );
+
add_settings_field(
'gutenberg-media-processing',
__( 'Client-side media processing', 'gutenberg' ),
diff --git a/lib/load.php b/lib/load.php
index 25b16b4e14b620..dc15dbb5635eb8 100644
--- a/lib/load.php
+++ b/lib/load.php
@@ -91,6 +91,7 @@ function gutenberg_is_experiment_enabled( $name ) {
require __DIR__ . '/experimental/synchronization.php';
require __DIR__ . '/experimental/script-modules.php';
require __DIR__ . '/experimental/posts/load.php';
+require __DIR__ . '/experimental/commands.php';
if ( gutenberg_is_experiment_enabled( 'gutenberg-no-tinymce' ) ) {
require __DIR__ . '/experimental/disable-tinymce.php';
diff --git a/packages/core-commands/README.md b/packages/core-commands/README.md
index a7f8073c1f1dec..ed2053cd18b9d7 100644
--- a/packages/core-commands/README.md
+++ b/packages/core-commands/README.md
@@ -16,6 +16,10 @@ _This package assumes that your code will run in an **ES2015+** environment. If
+### initializeCommandPalette
+
+Initializes the Command Palette.
+
### privateApis
Undocumented declaration.
diff --git a/packages/core-commands/src/index.js b/packages/core-commands/src/index.js
index 94878a556278a4..08be34995bc5ad 100644
--- a/packages/core-commands/src/index.js
+++ b/packages/core-commands/src/index.js
@@ -1 +1,43 @@
+/**
+ * WordPress dependencies
+ */
+import { createRoot, StrictMode } from '@wordpress/element';
+import { privateApis as routerPrivateApis } from '@wordpress/router';
+import { CommandMenu } from '@wordpress/commands';
+
+/**
+ * Internal dependencies
+ */
+import { useAdminNavigationCommands } from './admin-navigation-commands';
+import { useSiteEditorNavigationCommands } from './site-editor-navigation-commands';
+import { unlock } from './lock-unlock';
export { privateApis } from './private-apis';
+
+const { RouterProvider } = unlock( routerPrivateApis );
+
+// Register core commands and render the Command Palette.
+function CommandPalette() {
+ useAdminNavigationCommands();
+ useSiteEditorNavigationCommands();
+ return (
+
+
+
+ );
+}
+
+/**
+ * Initializes the Command Palette.
+ */
+export function initializeCommandPalette() {
+ if ( ! globalThis.IS_GUTENBERG_PLUGIN ) {
+ return;
+ }
+ const root = document.createElement( 'div' );
+ document.body.appendChild( root );
+ createRoot( root ).render(
+
+
+
+ );
+}
diff --git a/packages/router/src/router.tsx b/packages/router/src/router.tsx
index 8fbf6e609fc324..c1eb6b46699404 100644
--- a/packages/router/src/router.tsx
+++ b/packages/router/src/router.tsx
@@ -221,7 +221,7 @@ export function RouterProvider( {
);
const matcher = useMemo( () => {
const ret = new RouteRecognizer();
- routes.forEach( ( route ) => {
+ ( routes ?? [] ).forEach( ( route ) => {
ret.add( [ { path: route.path, handler: route } ], {
as: route.name,
} );