Docs / SDK / Commands & Hotkeys
Commands & Hotkeys
Commands are named actions that can be triggered from the command palette, menu items, toolbar buttons, or keyboard shortcuts. The command system connects them all through a single dispatcher.
Defining commands
Commands are defined as part of a module's ModuleDef. Each command has an ID, label, optional icon, shortcut display, and category for grouping in the palette:
const commands = [
{
id: "crm.new-contact",
label: "New Contact",
icon: "user-plus",
shortcut: "Cmd+N",
category: "CRM",
},
{
id: "crm.search-contacts",
label: "Search Contacts",
icon: "search",
shortcut: "Cmd+F",
category: "CRM",
},
{
id: "crm.import-csv",
label: "Import from CSV",
icon: "upload",
category: "CRM",
},
];Command dispatcher
The CommandDispatcherProvider manages command registration and execution. Wrap your app with it and register handlers for each command:
import { CommandDispatcherProvider, useCommandDispatcher } from "@substrateui/ui/commands";
// In your app root
<CommandDispatcherProvider commands={allCommands}>
<App />
</CommandDispatcherProvider>
// In any component, register handlers and execute commands
function ContactsPage() {
const { registerHandler, execute } = useCommandDispatcher();
useEffect(() => {
registerHandler("crm.new-contact", async () => {
setCreateDialogOpen(true);
});
registerHandler("crm.search-contacts", async () => {
searchInputRef.current?.focus();
});
return () => {
unregisterHandler("crm.new-contact");
unregisterHandler("crm.search-contacts");
};
}, []);
return (
<button onClick={() => execute("crm.new-contact")}>
New Contact
</button>
);
}useCommandDispatcher
The hook returns the full dispatcher API:
| Method | Description |
|---|---|
| registerHandler(id, handler) | Register a handler function for a command ID |
| unregisterHandler(id) | Remove a handler for a command ID |
| execute(id, args?) | Execute a command by ID with optional arguments |
| getCommands() | Get all registered command definitions |
| getRecent() | Get recently executed command IDs |
| search(query) | Search commands by label or category |
Command palette
The command palette is a searchable overlay (Cmd+K) that lists all available commands grouped by category. It reads from the dispatcher and executes commands on selection:
import { CommandPalette } from "@substrateui/ui/commands";
function App() {
const [open, setOpen] = useState(false);
// Cmd+K to open
useHotkey("mod+k", () => setOpen(true));
return (
<CommandPalette
open={open}
onOpenChange={setOpen}
placeholder="Type a command..."
showShortcuts
groupByCategory
/>
);
}Hotkeys
Hotkeys are keyboard shortcuts defined as part of a module. They map a key combination to a command ID:
// In a module definition
const hotkeys = [
{ id: "crm.new", shortcut: "N", label: "New Contact", commandId: "crm.new-contact" },
{ id: "crm.search", shortcut: "Cmd+F", label: "Search", commandId: "crm.search-contacts" },
{ id: "crm.palette", shortcut: "Cmd+K", label: "Command Palette", commandId: "palette.open" },
];useHotkeys / useHotkey
Use useHotkeys() to register an array of hotkey definitions with handler functions, or useHotkey() for a single shortcut:
import { useHotkeys, useHotkey } from "@substrateui/ui/commands";
// Register multiple hotkeys from module definitions
useHotkeys(moduleHotkeys, {
"crm.new": () => setCreateDialogOpen(true),
"crm.search": () => searchInputRef.current?.focus(),
});
// Register a single shortcut
useHotkey("mod+k", () => setCommandPaletteOpen(true));
useHotkey("Escape", () => setCommandPaletteOpen(false), {
enabled: commandPaletteOpen,
});Hotkey options
| Option | Type | Description |
|---|---|---|
| enabled | boolean | Enable/disable the hotkey (default: true) |
| scope | string | Scope name for grouping |
| activeScopes | string[] | Only active when these scopes are active |
| target | HTMLElement | null | Target element (default: document) |
| preventDefault | boolean | Prevent default browser behavior |
How it all connects
Commands are the glue between UI surfaces. A single command like crm.new-contact can be triggered from:
- --Menu bar wsMenuItem with commandId
- --Toolbar toolBarButton with commandId
- --Command palette Cmd+K → search → select
- --Hotkey HotkeyDef with commandId
- --Code execute("crm.new-contact")
The dispatcher ensures that no matter how the command is triggered, the same handler runs.