App screens
Command palette shell
The command-menu pattern in the same persistent sidebar/header shell as the other App screens patterns: a search-style Button in the header opens a real CommandPalette (also bound to ⌘/Ctrl+K via useCommandPalette), with grouped, icon-labeled, keyword-searchable commands and a recent-items view when the query is empty.
How it's built
useCommandPalette wires the ⌘/Ctrl+K hotkey and open state; spread getPaletteProps() onto CommandPalette — the header's search Button just calls the same palette.open().
emptyQueryItems shows a curated "recent" list before the visitor types anything; once they type, the built-in fuzzy filter takes over and matches keywords beyond the visible label (try "bill").
The dialog traps focus, restores it to whichever control opened it (the header button or the ⌘K hotkey) on close, and closes on Escape.
Source
One file · copy & pasteimport { useState } from "react";
import { Button } from "oks-ui/button";
import { CommandPalette, useCommandPalette } from "oks-ui/command-palette";
import type { CommandPaletteItem } from "oks-ui/command-palette";
import type { NavItemData } from "oks-ui/nav";
import { PatternAppShell } from "../PatternAppShell";
import {
BriefcaseIcon,
CreditCardIcon,
GridIcon,
HelpCircleIcon,
PlusIcon,
ReceiptIcon,
SearchIcon,
SettingsIcon,
UsersIcon,
} from "../../showcase/icons";
// A dashed square with a plus — "invite teammate".
function UserPlusIcon({ size = 16 }: { size?: number }) {
return (
<svg width={size} height={size} viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.4" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
<circle cx="6.5" cy="6" r="2.5" />
<path d="M2 14c0-2.5 2-4 4.5-4s4.5 1.5 4.5 4" />
<path d="M13 5.5v4M11 7.5h4" />
</svg>
);
}
// A downward-pointing arrow into a tray — "export".
function ExportIcon({ size = 16 }: { size?: number }) {
return (
<svg width={size} height={size} viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.4" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
<path d="M8 2.5v7M5 6.5 8 9.5l3-3" />
<path d="M2.5 12v1.5A1.5 1.5 0 0 0 4 15h8a1.5 1.5 0 0 0 1.5-1.5V12" />
</svg>
);
}
// A moon — "toggle theme".
function MoonIcon({ size = 16 }: { size?: number }) {
return (
<svg width={size} height={size} viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.4" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
<path d="M13.5 9.5a5.5 5.5 0 1 1-7-7 4.3 4.3 0 0 0 7 7Z" />
</svg>
);
}
const NAV_ITEMS: NavItemData[] = [
{
key: "main",
label: "Main menu",
isSection: true,
children: [
{ key: "dashboard", label: "Dashboard", icon: <GridIcon size={18} /> },
{ key: "projects", label: "Projects", icon: <BriefcaseIcon size={18} /> },
{ key: "invoices", label: "Invoices", icon: <ReceiptIcon size={18} /> },
{ key: "team", label: "Team", icon: <UsersIcon size={18} /> },
],
},
{
key: "other",
label: "Other",
isSection: true,
children: [
{ key: "billing", label: "Billing", icon: <CreditCardIcon size={18} /> },
{ key: "settings", label: "Settings", icon: <SettingsIcon size={18} /> },
],
},
];
const ITEMS: CommandPaletteItem[] = [
{ id: "new-invoice", label: "Create invoice", group: "Actions", icon: <ReceiptIcon size={16} />, shortcut: ["⌘", "N"], keywords: ["bill", "new"] },
{ id: "invite", label: "Invite teammate", group: "Actions", icon: <UserPlusIcon />, keywords: ["team", "member"] },
{ id: "export", label: "Export data as CSV", group: "Actions", icon: <ExportIcon />, keywords: ["download"] },
{ id: "theme", label: "Toggle dark mode", group: "Actions", icon: <MoonIcon />, keywords: ["theme", "appearance"] },
{ id: "dashboard", label: "Go to Dashboard", group: "Navigation", icon: <GridIcon size={16} />, keywords: ["home", "overview"] },
{ id: "projects", label: "Go to Projects", group: "Navigation", icon: <BriefcaseIcon size={16} /> },
{ id: "team", label: "Go to Team", group: "Navigation", icon: <UsersIcon size={16} /> },
{ id: "billing", label: "Go to Billing & plans", group: "Navigation", icon: <CreditCardIcon size={16} /> },
{ id: "settings", label: "Open Settings", group: "Navigation", icon: <SettingsIcon size={16} /> },
{ id: "help", label: "Search the help center", group: "Support", icon: <HelpCircleIcon size={16} /> },
];
const RECENT_IDS = ["dashboard", "new-invoice", "team"];
export function CommandPaletteShell() {
const [lastAction, setLastAction] = useState<string | null>(null);
const palette = useCommandPalette();
const recentItems = ITEMS.filter((item) => RECENT_IDS.includes(item.id));
return (
<>
<PatternAppShell
navItems={NAV_ITEMS}
selectedNavKey="dashboard"
headerLeft={
<Button
variant="bordered"
size="sm"
onClick={palette.open}
startContent={<SearchIcon size={15} />}
className="w-full max-w-xs justify-between text-ink-faint"
endContent={
<span className="flex items-center gap-0.5 rounded-[var(--r-sm)] bg-paper-sunken px-1.5 py-0.5 text-[0.7rem] font-medium text-ink-faint">
⌘K
</span>
}
>
Search or jump to…
</Button>
}
contentClassName="flex flex-1 flex-col items-center justify-center gap-4 overflow-y-auto p-8 text-center"
>
<span className="flex h-14 w-14 items-center justify-center rounded-[var(--r-lg)] bg-[color:var(--oks-color-primary-50)] text-[color:var(--oks-color-primary-700)]">
<SearchIcon size={24} />
</span>
<div>
<p className="text-[1rem] font-semibold text-ink">Find anything, instantly</p>
<p className="mt-1 max-w-xs text-[0.85rem] text-ink-muted">
Press <kbd className="rounded-[var(--r-sm)] bg-paper-sunken px-1.5 py-0.5 font-sans text-[0.8rem]">⌘K</kbd> or the search bar above to jump to a
page, run an action, or search for help.
</p>
</div>
<Button color="primary" radius="full" startContent={<PlusIcon size={14} />} onClick={palette.open}>
Open command palette
</Button>
{lastAction ? <p className="text-[0.8rem] text-ink-faint">Last ran: {lastAction}</p> : null}
</PatternAppShell>
<CommandPalette
{...palette.getPaletteProps()}
items={ITEMS}
emptyQueryItems={recentItems}
placeholder="Search commands, pages, and help…"
onSelect={(item) => setLastAction(item.label)}
/>
</>
);
}