A powerful command menu plugin for Payload CMS that enhances navigation and accessibility within the admin panel. Quickly search and navigate through collections, globals, and custom actions using keyboard shortcuts.
✨ Quick Search - Instantly search across all collections and globals
⌨️ Keyboard Shortcuts - Fully customizable keyboard shortcuts powered by react-hotkeys-hook
🔍 Collection Submenu - Search within collection documents by their title field
🎨 Custom Icons - Use any Lucide icon for collections and globals
🎯 Custom Items - Add custom actions and menu groups
🌍 i18n Support - Built-in English and Ukrainian translations, easily add your own
🖥️ Cross-platform - Optimized shortcuts for both macOS and Windows/Linux
npm install @veiag/payload-cmdk
# or
yarn add @veiag/payload-cmdk
# or
pnpm add @veiag/payload-cmdkThe plugin works out of the box with minimal configuration:
import { payloadCmdk } from '@veiag/payload-cmdk'
import { buildConfig } from 'payload'
export default buildConfig({
// ... your config
plugins: [
payloadCmdk({
// Plugin works without any options!
}),
],
})This will:
- Add a search button to the admin panel
- Enable
⌘K(Mac) /Ctrl+K(Windows/Linux) keyboard shortcut - List all collections and globals in the command menu
- Enable collection submenu search
import { payloadCmdk } from '@veiag/payload-cmdk'
import { buildConfig } from 'payload'
export default buildConfig({
plugins: [
payloadCmdk({
// Keyboard shortcut to open the menu
shortcut: ['meta+k', 'ctrl+k'], // Default
// Search button configuration
searchButton: {
position: 'actions', // 'actions' | 'nav'
},
// Backdrop blur effect
blurBg: true, // Default
// Collection submenu configuration
submenu: {
enabled: true, // Default
shortcut: 'shift+enter', // 'shift+enter' | 'enter'
icons: {
posts: 'FileText',
users: 'User',
},
},
// Custom icons for collections and globals
icons: {
collections: {
posts: 'FileText',
pages: 'File',
media: 'Image',
users: 'Users',
},
globals: {
settings: 'Settings',
navigation: 'Menu',
},
},
// Collections/globals to ignore
slugsToIgnore: ['payload-migrations', 'payload-preferences'],
// Custom menu items
customItems: [
{
type: 'group',
title: 'Quick Actions',
items: [
{
type: 'item',
slug: 'view-site',
label: 'View Site',
icon: 'ExternalLink',
action: {
type: 'link',
href: 'https://your-site.com',
},
},
{
type: 'item',
slug: 'clear-cache',
label: 'Clear Cache',
icon: 'Trash2',
action: {
type: 'api',
method: 'POST',
href: '/api/cache/clear',
},
},
],
},
],
// Disable the plugin
disabled: false, // Default
}),
],
})Keyboard shortcut to open the command menu. Powered by react-hotkeys-hook.
- Type:
string | string[] - Default:
['meta+k', 'ctrl+k']
The default provides cross-platform support:
meta+k- Works on macOS (⌘K)ctrl+k- Works on Windows/Linux (Ctrl+K)
Examples:
// Single shortcut
shortcut: 'ctrl+shift+k'
// Multiple shortcuts for cross-platform support
shortcut: ['meta+k', 'ctrl+k']
// Custom combinations
shortcut: ['meta+/', 'ctrl+/']Configuration for the search button displayed in the admin panel.
- Type:
{ position?: 'actions' | 'nav' } | false - Default:
{ position: 'actions' }
Options:
position: 'actions'- Display in the action buttons area (default)position: 'nav'- Display in the navigation sidebarfalse- Hide the search button completely (keyboard shortcut still works)
Examples:
// Display in navigation
searchButton: {
position: 'nav'
}
// Hide search button
searchButton: falseActions button position:
Navigation button position:
Enable backdrop blur effect when the command menu is open.
- Type:
boolean - Default:
true
blurBg: false // Disable blur effectConfigure submenu behavior for searching within collection documents.
- Type:
object - Default:
{ enabled: true, shortcut: 'shift+enter' }
Options:
| Property | Type | Default | Description |
|---|---|---|---|
enabled |
boolean |
true |
Enable/disable submenu functionality |
shortcut |
'shift+enter' | 'enter' |
'shift+enter' |
Keyboard shortcut to open submenu |
icons |
object |
undefined |
Custom icons for collection submenus |
Shortcut behavior:
shift+enter: Shift+Enter opens submenu, Enter navigates to collection listenter: Enter opens submenu, Shift+Enter navigates to collection list
Example:
submenu: {
enabled: true,
shortcut: 'enter',
icons: {
posts: 'FileText',
products: 'ShoppingCart',
}
}The submenu searches documents by their useAsTitle field (or id if not specified). You can configure this in your collection:
{
slug: 'posts',
admin: {
useAsTitle: 'title' // Submenu will search by this field
}
}Customize icons for collections and globals using Lucide icon names.
- Type:
object - Default:
{ collections: {}, globals: {} }
Default icons:
- Collections:
Filesicon - Globals:
Globeicon
Example:
icons: {
collections: {
posts: 'FileText',
pages: 'File',
media: 'Image',
users: 'Users',
categories: 'Folder',
},
globals: {
settings: 'Settings',
navigation: 'Menu',
footer: 'Layout',
}
}Browse all available icons at lucide.dev/icons.
Add custom menu items and groups to the command menu.
- Type:
Array<CustomMenuItem | CustomMenuGroup> - Default:
[]
{
type: 'item',
slug: 'unique-slug',
label: 'Item Label', // Can be localized
icon: 'LucideIconName', // Optional, from lucide.dev/icons
action: {
type: 'link' | 'api',
href: '/path/or/url',
method?: 'GET' | 'POST' | 'PUT' | 'DELETE', // For API actions
body?: { key: 'value' } // For API actions
}
}{
type: 'group',
title: 'Group Title', // Can be localized
items: [
// Array of CustomMenuItem
]
}Example with localization:
customItems: [
{
type: 'group',
title: {
en: 'Quick Actions',
uk: 'Швидкі дії',
},
items: [
{
type: 'item',
slug: 'view-site',
label: {
en: 'View Site',
uk: 'Переглянути сайт',
},
icon: 'ExternalLink',
action: {
type: 'link',
href: 'https://your-site.com',
},
},
{
type: 'item',
slug: 'regenerate',
label: 'Regenerate Cache',
icon: 'RefreshCw',
action: {
type: 'api',
method: 'POST',
href: '/api/cache/regenerate',
},
},
],
},
]Specify which collection/global slugs to exclude from the command menu.
- Type:
CollectionSlug[] | { ignoreList: CollectionSlug[], replaceDefaults?: boolean } - Default:
['payload-migrations', 'payload-preferences', 'payload-locked-documents']
Examples:
// Add to default ignore list
slugsToIgnore: ['internal-collection', 'test-data']
// Replace default ignore list completely
slugsToIgnore: {
ignoreList: ['my-hidden-collection'],
replaceDefaults: true
}Completely disable the plugin.
- Type:
boolean - Default:
false
disabled: process.env.DISABLE_COMMAND_MENU === 'true'The plugin includes built-in translations for:
- 🇬🇧 English (
en) - 🇺🇦 Ukrainian (
uk)
You can add translations for other languages using Payload's i18n configuration:
import { buildConfig } from 'payload'
export default buildConfig({
i18n: {
supportedLanguages: {
//You can learn more about adding languages in the Payload docs
en,
uk,
de,
fr,
},
translations: {
de: {
cmdkPlugin: {
loading: 'Lädt...',
navigate: 'zum Navigieren',
noResults: 'Keine Ergebnisse gefunden',
open: 'zum Öffnen',
search: 'Sammlungen, Globals durchsuchen...',
searchIn: 'Suchen in {{label}}',
searchInCollection: 'in Sammlung suchen',
searchShort: 'Suchen',
},
},
fr: {
cmdkPlugin: {
loading: 'Chargement...',
navigate: 'pour naviguer',
noResults: 'Aucun résultat trouvé',
open: 'pour ouvrir',
search: 'Rechercher collections, globals...',
searchIn: 'Rechercher dans {{label}}',
searchInCollection: 'pour rechercher dans la collection',
searchShort: 'Rechercher',
},
},
},
},
plugins: [
payloadCmdk({
// Your config
}),
],
})All translation keys are under the cmdkPlugin namespace:
| Key | Description | Example (EN) |
|---|---|---|
search |
Main search placeholder | "Search collections, globals..." |
searchShort |
Short search label | "Search" |
searchIn |
Submenu search placeholder | "Search in {{label}}" |
loading |
Loading state | "Loading..." |
noResults |
No results state | "No results found" |
navigate |
Footer hint for navigation | "to navigate" |
searchInCollection |
Footer hint for collection search | "to search in collection" |
open |
Footer hint for opening documents | "to open" |
| Shortcut | Action |
|---|---|
⌘K / Ctrl+K |
Open/close command menu |
Esc |
Close menu or go back in submenu |
↑ ↓ |
Navigate items |
Enter |
Select item or navigate to collection |
Shift+Enter |
Search within collection (default) |
| Shortcut | Action |
|---|---|
Esc |
Go back to main menu |
Enter |
Open selected document |
export default buildConfig({
plugins: [payloadCmdk()],
})export default buildConfig({
plugins: [
payloadCmdk({
shortcut: ['meta+/', 'ctrl+/'],
searchButton: false, // Hide button, only use keyboard
}),
],
})export default buildConfig({
plugins: [
payloadCmdk({
customItems: [
{
type: 'item',
slug: 'documentation',
label: 'View Documentation',
icon: 'BookOpen',
action: {
type: 'link',
href: 'https://docs.your-site.com',
},
},
],
}),
],
})export default buildConfig({
plugins: [
payloadCmdk({
icons: {
collections: {
posts: 'Newspaper',
pages: 'FileText',
media: 'Image',
categories: 'FolderTree',
tags: 'Tag',
users: 'UserCircle',
comments: 'MessageCircle',
},
globals: {
header: 'LayoutTemplate',
footer: 'Layout',
settings: 'Settings',
navigation: 'Menu',
seo: 'Search',
},
},
submenu: {
enabled: true,
icons: {
posts: 'FileText',
pages: 'File',
media: 'Image',
},
},
}),
],
})Objects are not valid as a React childerror: Ensure youradmin.useAsTitlefield is a string and not an object. Currently, plugin doesn't have any safeguards for non-string title fields.
Contributions are welcome! Please feel free to submit a Pull Request.
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add some amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
Found a bug or have a feature request? Please open an issue on GitHub.
MIT © VeiaG



