Skip to content

feat(ui): add command palette to admin dashboard#16798

Merged
denolfe merged 38 commits into
mainfrom
feat/cmd-k-command-palette
Jun 2, 2026
Merged

feat(ui): add command palette to admin dashboard#16798
denolfe merged 38 commits into
mainfrom
feat/cmd-k-command-palette

Conversation

@denolfe

@denolfe denolfe commented May 29, 2026

Copy link
Copy Markdown
Member

Overview

Initial functionality for Command Palette via cmd+k.

  • Adds a keyboard-triggered command palette to the Payload admin.
  • Shortcuts:
    • cmd/ctrl+K opens it
    • Enter navigates to collection/global
    • cmd+Enter creates new document for a collection
    • esc or clicking outside closes
  • Users fuzzy-search the collections and globals they can access, open a list or global view with Enter, and create a new document with cmd+Enter.
CleanShot.2026-06-01.at.09.58.55.mp4

Video shows cmd+k trigger, collection filtering, Enter to navigate, and cmd+Enter to create

Key Changes

  • Reuses v4 styling
  • Builds actions from config, permissions, and visible entities.
  • Dependency-free fuzzy matcher
  • Translation keys
  • e2e coverage
  • Shared nav-visibility predicate.
    • Extracts the "is this entity shown to this user" rule (admin.group !== false plus read permission) into a new isNavEntityVisible utility in packages/ui. Both the sidebar nav (groupNavItems) and the palette (buildActions) call it, so the two surfaces decide visibility from one definition instead of duplicated inline checks.

Design Decisions

  • Entity-row result model. One row per entity rather than flat action-type groups. Enter is the primary action (open list / open global); cmd+Enter creates a new document, hinted inline on the active row and only when the user has create permission. Globals have no create action.
  • Access control is client-side. Only entities the user can read appear, derived from useAuth().permissions. The palette and the sidebar nav share one visibility predicate (isNavEntityVisible), so the lists can't drift apart. The create shortcut is gated on create permission.
  • Mounted in the Default template, scoped to visible entities. Mounting here (rather than in RootProvider) lets the palette respect admin.hidden / visibleEntities, so hidden entities never surface.
  • CSS, not SCSS. Follows the v4 styling direction already underway in packages/ui.

Accepted limitations for this phase:

  • cmd/ctrl+K does not open the palette while a drawer or modal is already open. This is a current useHotkey edit-depth limitation, not specific to this feature.

Overall Flow

sequenceDiagram
    participant User
    participant Hotkey as useHotkey
    participant Palette as CommandPalette
    participant Build as buildActions
    participant Filter as filterActions (fuzzyMatch)
    participant Router

    User->>Hotkey: cmd/ctrl+K
    Hotkey->>Palette: open
    Palette->>Build: config + permissions + visibleEntities
    Build-->>Palette: grouped actions (visibility via isNavEntityVisible)
    User->>Palette: type query
    Palette->>Filter: groups + query
    Filter-->>Palette: ranked actions with match indices
    User->>Palette: ArrowUp/Down, then Enter (or Cmd+Enter)
    Palette->>Router: push(formatAdminURL(...))
    Palette->>Palette: close
Loading

Future Changes

Not in scope for this PR but will be fast follows if this is merged in:

  • Custom component slots
  • Recent/Favorites component
  • Contextual actions menu via cmd+k on a row

@github-actions

github-actions Bot commented May 29, 2026

Copy link
Copy Markdown
Contributor

📦 esbuild Bundle Analysis for payload

This analysis was generated by esbuild-bundle-analyzer. 🤖

Meta File Out File Size (raw) Note
packages/next/meta_index.json esbuild/index.js 1.03 MB ⚠️ +177 B (+0.0%)
packages/payload/meta_index.json esbuild/index.js 1.41 MB ⚠️ +84 B (+0.0%)
packages/payload/meta_shared.json esbuild/exports/shared.js 192.51 KB ✅ No change
packages/richtext-lexical/meta_client.json esbuild/exports/client_optimized/index.js 304.12 KB ⚠️ +1 B (+0.0%)
packages/ui/meta_client.json esbuild/exports/client_optimized/index.js 1.28 MB ⚠️ +9.92 KB (+0.8%)
packages/ui/meta_shared.json esbuild/exports/shared_optimized/index.js 18.64 KB ⚠️ +87 B (+0.5%)
Largest paths These visualization shows top 20 largest paths in the bundle.

Meta file: packages/next/meta_index.json, Out file: esbuild/index.js

Path Size
../../node_modules ${{\color{Goldenrod}{ █████████████████████ }}}$ 84.0%, 861.10 KB
dist/views/Version ${{\color{Goldenrod}{ █▎ }}}$ 5.3%, 54.61 KB
dist/views/Dashboard ${{\color{Goldenrod}{ ▌ }}}$ 2.1%, 21.85 KB
dist/views/Document ${{\color{Goldenrod}{ ▍ }}}$ 1.6%, 16.76 KB
dist/views/List ${{\color{Goldenrod}{ ▍ }}}$ 1.5%, 15.69 KB
dist/views/Root ${{\color{Goldenrod}{ ▎ }}}$ 1.0%, 10.01 KB
dist/views/Account ${{\color{Goldenrod}{ ▏ }}}$ 0.6%, 6.13 KB
dist/views/API ${{\color{Goldenrod}{ ▏ }}}$ 0.6%, 6.13 KB
dist/views/Versions ${{\color{Goldenrod}{ ▏ }}}$ 0.6%, 5.74 KB
dist/views/Login ${{\color{Goldenrod}{ ▏ }}}$ 0.5%, 4.62 KB
dist/layouts/Root ${{\color{Goldenrod}{ }}}$ 0.4%, 3.61 KB
dist/views/ForgotPassword ${{\color{Goldenrod}{ }}}$ 0.3%, 3.22 KB
dist/views/CreateFirstUser ${{\color{Goldenrod}{ }}}$ 0.3%, 3.02 KB
dist/views/ResetPassword ${{\color{Goldenrod}{ }}}$ 0.2%, 2.46 KB
dist/views/Logout ${{\color{Goldenrod}{ }}}$ 0.2%, 1.96 KB
dist/views/Verify ${{\color{Goldenrod}{ }}}$ 0.1%, 1.35 KB
dist/utilities/initReq.js ${{\color{Goldenrod}{ }}}$ 0.1%, 1.15 KB
dist/views/NotFound ${{\color{Goldenrod}{ }}}$ 0.1%, 1.04 KB
dist/adapters/router.js ${{\color{Goldenrod}{ }}}$ 0.1%, 659 B
dist/adapters/server.js ${{\color{Goldenrod}{ }}}$ 0.1%, 531 B
(other) ${{\color{Goldenrod}{ ████ }}}$ 16.0%, 164.11 KB

Meta file: packages/payload/meta_index.json, Out file: esbuild/index.js

Path Size
../../node_modules ${{\color{Goldenrod}{ █████████████████ }}}$ 68.4%, 959.51 KB
dist/fields/hooks ${{\color{Goldenrod}{ ▊ }}}$ 3.1%, 44.07 KB
dist/collections/operations ${{\color{Goldenrod}{ ▋ }}}$ 2.9%, 40.23 KB
dist/versions/migrations ${{\color{Goldenrod}{ ▎ }}}$ 1.3%, 18.50 KB
dist/auth/operations ${{\color{Goldenrod}{ ▎ }}}$ 1.1%, 15.63 KB
dist/fields/config ${{\color{Goldenrod}{ ▎ }}}$ 1.0%, 13.40 KB
dist/globals/operations ${{\color{Goldenrod}{ ▎ }}}$ 1.0%, 13.40 KB
dist/utilities/configToJSONSchema.js ${{\color{Goldenrod}{ ▏ }}}$ 0.9%, 13.10 KB
dist/queues/operations ${{\color{Goldenrod}{ ▏ }}}$ 0.9%, 12.63 KB
dist/fields/validations.js ${{\color{Goldenrod}{ ▏ }}}$ 0.8%, 10.57 KB
dist/collections/config ${{\color{Goldenrod}{ ▏ }}}$ 0.7%, 9.53 KB
dist/bin/generateImportMap ${{\color{Goldenrod}{ ▏ }}}$ 0.7%, 9.44 KB
dist/config/orderable ${{\color{Goldenrod}{ ▏ }}}$ 0.6%, 7.92 KB
dist/uploads/fetchAPI-multipart ${{\color{Goldenrod}{ ▏ }}}$ 0.6%, 7.80 KB
dist/index.js ${{\color{Goldenrod}{ ▏ }}}$ 0.6%, 7.77 KB
dist/hierarchy/utils ${{\color{Goldenrod}{ ▏ }}}$ 0.5%, 7.65 KB
dist/database/migrations ${{\color{Goldenrod}{ ▏ }}}$ 0.5%, 7.54 KB
dist/collections/endpoints ${{\color{Goldenrod}{ }}}$ 0.4%, 6.23 KB
dist/auth/strategies ${{\color{Goldenrod}{ }}}$ 0.4%, 5.43 KB
dist/queues/config ${{\color{Goldenrod}{ }}}$ 0.4%, 5.31 KB
(other) ${{\color{Goldenrod}{ ███████▉ }}}$ 31.6%, 443.35 KB

Meta file: packages/payload/meta_shared.json, Out file: esbuild/exports/shared.js

Path Size
../../node_modules ${{\color{Goldenrod}{ ███████████████████▉ }}}$ 79.5%, 150.12 KB
dist/fields/validations.js ${{\color{Goldenrod}{ █▍ }}}$ 5.6%, 10.57 KB
dist/config/orderable ${{\color{Goldenrod}{ ▍ }}}$ 1.7%, 3.13 KB
dist/fields/baseFields ${{\color{Goldenrod}{ ▍ }}}$ 1.5%, 2.79 KB
dist/utilities/deepCopyObject.js ${{\color{Goldenrod}{ ▎ }}}$ 1.3%, 2.54 KB
dist/auth/cookies.js ${{\color{Goldenrod}{ ▏ }}}$ 0.8%, 1.55 KB
dist/utilities/flattenTopLevelFields.js ${{\color{Goldenrod}{ ▏ }}}$ 0.8%, 1.42 KB
dist/fields/config ${{\color{Goldenrod}{ ▏ }}}$ 0.7%, 1.29 KB
dist/utilities/getVersionsConfig.js ${{\color{Goldenrod}{ ▏ }}}$ 0.6%, 1.04 KB
dist/utilities/flattenAllFields.js ${{\color{Goldenrod}{ ▏ }}}$ 0.5%, 943 B
dist/utilities/unflatten.js ${{\color{Goldenrod}{ }}}$ 0.4%, 779 B
dist/utilities/sanitizeUserDataForEmail.js ${{\color{Goldenrod}{ }}}$ 0.4%, 713 B
dist/utilities/getFieldPermissions.js ${{\color{Goldenrod}{ }}}$ 0.3%, 651 B
dist/collections/config ${{\color{Goldenrod}{ }}}$ 0.3%, 570 B
dist/bin/generateImportMap ${{\color{Goldenrod}{ }}}$ 0.3%, 561 B
dist/auth/sessions.js ${{\color{Goldenrod}{ }}}$ 0.3%, 525 B
dist/fields/getFieldPaths.js ${{\color{Goldenrod}{ }}}$ 0.3%, 485 B
dist/utilities/appendDateTimezoneSelectFields.js ${{\color{Goldenrod}{ }}}$ 0.2%, 451 B
dist/utilities/getSafeRedirect.js ${{\color{Goldenrod}{ }}}$ 0.2%, 423 B
dist/utilities/deepMerge.js ${{\color{Goldenrod}{ }}}$ 0.2%, 413 B
(other) ${{\color{Goldenrod}{ █████▏ }}}$ 20.5%, 38.61 KB

Meta file: packages/richtext-lexical/meta_client.json, Out file: esbuild/exports/client_optimized/index.js

Path Size
dist/features/blocks ${{\color{Goldenrod}{ ███ }}}$ 12.4%, 37.36 KB
dist/lexical/ui ${{\color{Goldenrod}{ ██▊ }}}$ 11.4%, 34.16 KB
dist/lexical/plugins ${{\color{Goldenrod}{ ██▋ }}}$ 10.9%, 32.88 KB
dist/features/experimental_table ${{\color{Goldenrod}{ ██▎ }}}$ 9.0%, 27.16 KB
dist/packages/@lexical ${{\color{Goldenrod}{ █▌ }}}$ 6.3%, 18.99 KB
dist/features/link ${{\color{Goldenrod}{ █▌ }}}$ 6.3%, 18.81 KB
dist/features/toolbars ${{\color{Goldenrod}{ █▍ }}}$ 5.5%, 16.58 KB
dist/features/upload ${{\color{Goldenrod}{ █▏ }}}$ 4.7%, 14.09 KB
dist/features/textState ${{\color{Goldenrod}{ ▉ }}}$ 3.7%, 11.08 KB
dist/features/relationship ${{\color{Goldenrod}{ ▊ }}}$ 3.1%, 9.39 KB
dist/lexical/utils ${{\color{Goldenrod}{ ▋ }}}$ 2.9%, 8.79 KB
dist/features/converters ${{\color{Goldenrod}{ ▋ }}}$ 2.8%, 8.36 KB
dist/features/debug ${{\color{Goldenrod}{ ▋ }}}$ 2.5%, 7.40 KB
dist/utilities/fieldsDrawer ${{\color{Goldenrod}{ ▌ }}}$ 2.4%, 7.29 KB
dist/lexical/config ${{\color{Goldenrod}{ ▍ }}}$ 1.7%, 5.08 KB
dist/features/lists ${{\color{Goldenrod}{ ▍ }}}$ 1.7%, 5.00 KB
dist/features/format ${{\color{Goldenrod}{ ▎ }}}$ 1.2%, 3.46 KB
dist/lexical/LexicalEditor.js ${{\color{Goldenrod}{ ▎ }}}$ 1.1%, 3.23 KB
dist/features/horizontalRule ${{\color{Goldenrod}{ ▎ }}}$ 1.1%, 3.18 KB
dist/field/Field.js ${{\color{Goldenrod}{ ▏ }}}$ 0.9%, 2.84 KB
(other) ${{\color{Goldenrod}{ █████████████████████▉ }}}$ 87.6%, 263.53 KB

Meta file: packages/ui/meta_client.json, Out file: esbuild/exports/client_optimized/index.js

Path Size
../../node_modules ${{\color{Goldenrod}{ ███████████▍ }}}$ 45.6%, 579.79 KB
dist/elements/Hierarchy ${{\color{Goldenrod}{ ▉ }}}$ 3.5%, 44.03 KB
dist/elements/BulkUpload ${{\color{Goldenrod}{ ▌ }}}$ 2.2%, 28.07 KB
dist/views/HierarchyList ${{\color{Goldenrod}{ ▍ }}}$ 1.6%, 20.43 KB
dist/elements/Table ${{\color{Goldenrod}{ ▍ }}}$ 1.5%, 19.28 KB
dist/views/Edit ${{\color{Goldenrod}{ ▎ }}}$ 1.4%, 17.57 KB
dist/elements/WhereBuilder ${{\color{Goldenrod}{ ▎ }}}$ 1.4%, 17.34 KB
dist/forms/Form ${{\color{Goldenrod}{ ▎ }}}$ 1.2%, 15.87 KB
dist/fields/Relationship ${{\color{Goldenrod}{ ▎ }}}$ 1.2%, 15.47 KB
dist/fields/Blocks ${{\color{Goldenrod}{ ▎ }}}$ 1.2%, 15.11 KB
dist/fields/Upload ${{\color{Goldenrod}{ ▎ }}}$ 1.1%, 14.40 KB
dist/elements/QueryPresets ${{\color{Goldenrod}{ ▏ }}}$ 0.8%, 10.22 KB
dist/elements/PublishButton ${{\color{Goldenrod}{ ▏ }}}$ 0.7%, 8.66 KB
dist/elements/HTMLDiff ${{\color{Goldenrod}{ ▏ }}}$ 0.7%, 8.38 KB
dist/elements/LivePreview ${{\color{Goldenrod}{ ▏ }}}$ 0.6%, 8.26 KB
dist/elements/ReactSelect ${{\color{Goldenrod}{ ▏ }}}$ 0.6%, 7.84 KB
dist/fields/Array ${{\color{Goldenrod}{ ▏ }}}$ 0.6%, 7.76 KB
dist/views/List ${{\color{Goldenrod}{ ▏ }}}$ 0.6%, 7.75 KB
dist/elements/Nav ${{\color{Goldenrod}{ ▏ }}}$ 0.6%, 7.54 KB
dist/elements/RelationshipTable ${{\color{Goldenrod}{ ▏ }}}$ 0.5%, 6.69 KB
(other) ${{\color{Goldenrod}{ █████████████▌ }}}$ 54.4%, 691.23 KB

Meta file: packages/ui/meta_shared.json, Out file: esbuild/exports/shared_optimized/index.js

Path Size
dist/graphics/Logo ${{\color{Goldenrod}{ ███████▋ }}}$ 30.9%, 5.57 KB
../../node_modules ${{\color{Goldenrod}{ ███▋ }}}$ 14.7%, 2.65 KB
dist/graphics/Icon ${{\color{Goldenrod}{ ██▏ }}}$ 8.5%, 1.52 KB
dist/utilities/formatDocTitle ${{\color{Goldenrod}{ █▊ }}}$ 7.4%, 1.32 KB
dist/providers/TableColumns ${{\color{Goldenrod}{ █▏ }}}$ 4.8%, 866 B
dist/utilities/getGlobalData.js ${{\color{Goldenrod}{ █ }}}$ 4.2%, 762 B
dist/utilities/api.js ${{\color{Goldenrod}{ █ }}}$ 4.2%, 756 B
dist/utilities/groupNavItems.js ${{\color{Goldenrod}{ █ }}}$ 4.1%, 745 B
dist/elements/Translation ${{\color{Goldenrod}{ ▋ }}}$ 2.7%, 493 B
dist/utilities/handleTakeOver.js ${{\color{Goldenrod}{ ▌ }}}$ 2.4%, 440 B
dist/utilities/traverseForLocalizedFields.js ${{\color{Goldenrod}{ ▌ }}}$ 2.2%, 399 B
dist/elements/withMergedProps ${{\color{Goldenrod}{ ▍ }}}$ 1.9%, 339 B
dist/utilities/getNavGroups.js ${{\color{Goldenrod}{ ▍ }}}$ 1.9%, 338 B
dist/utilities/getVisibleEntities.js ${{\color{Goldenrod}{ ▍ }}}$ 1.8%, 329 B
dist/elements/WithServerSideProps ${{\color{Goldenrod}{ ▎ }}}$ 1.3%, 232 B
dist/utilities/handleGoBack.js ${{\color{Goldenrod}{ ▎ }}}$ 1.0%, 180 B
dist/fields/mergeFieldStyles.js ${{\color{Goldenrod}{ ▏ }}}$ 0.9%, 159 B
dist/utilities/handleBackToDashboard.js ${{\color{Goldenrod}{ ▏ }}}$ 0.8%, 152 B
dist/forms/Form ${{\color{Goldenrod}{ ▏ }}}$ 0.8%, 148 B
dist/utilities/abortAndIgnore.js ${{\color{Goldenrod}{ ▏ }}}$ 0.8%, 146 B
(other) ${{\color{Goldenrod}{ █████████████████▎ }}}$ 69.1%, 12.45 KB
Details

Next to the size is how much the size has increased or decreased compared with the base branch of this PR.

  • ‼️: Size increased by 20% or more. Special attention should be given to this.
  • ⚠️: Size increased in acceptable range (lower than 20%).
  • ✅: No change or even downsized.
  • 🗑️: The out file is deleted: not found in base branch.
  • 🆕: The out file is newly found: will be added to base branch.

denolfe added 7 commits May 29, 2026 16:02
Memoize the cmd+K handler so the global keydown listener no longer re-subscribes every render, key the scroll-into-view effect on the active option id, clamp the active index during render instead of in an effect, and replace per-row indexOf with a memoized lookup map.
…nput divider

Drop the navigate/close footer hints (keep open + create), close the palette when the backdrop around the panel is clicked, and replace the input's blue rounded focus outline with a straight muted bottom hairline.
The global :focus-visible rule applies a border-radius to the focused input, which rounded the ends of the new bottom hairline. Reset border-radius alongside the outline so the divider stays straight.
Mute the bottom hairline to 50% of --color-border via color-mix so it derives from the theme token and still flips for dark mode.
denolfe added 8 commits May 30, 2026 22:37
…s active

The active-option auto-scroll called scrollIntoView on the option element,
which aligned the option's top edge to the scroll container and clipped the
group label sitting directly above it — so headers like "Favorites" vanished
after scrolling down then back up. When the active option is the first in its
group, scroll its group label into view instead so the header stays visible.
Opening the palette (or actions menu) under a stationary cursor fired a
synthetic mouseenter that hijacked the keyboard's default selection. Use
onMouseMove instead, which only fires on actual pointer movement.
Make the Enter/Select hint the leftmost footer action (was after Create new
on collection rows) and give it a primary treatment, strong weight and
full-strength text/key color, so the default action stands out.

Ported from 2d9c6ab; adapted to this branch's footer (no row-actions menu)
and named runAction intent ('navigate'/'create').
@denolfe denolfe marked this pull request as ready for review June 1, 2026 14:11
Comment thread packages/ui/src/templates/Default/index.tsx
Comment thread packages/ui/src/elements/CommandPalette/index.tsx Outdated
@denolfe

denolfe commented Jun 2, 2026

Copy link
Copy Markdown
Member Author

In the future, the CommandPalette and EntityVisibilityProvider components should be moved above the Default template as an optimization but that would expand scope significatly. Will address in future.

@denolfe denolfe merged commit ff05d40 into main Jun 2, 2026
159 of 164 checks passed
@denolfe denolfe deleted the feat/cmd-k-command-palette branch June 2, 2026 15:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants