Skip to content
Drivn
4 min read

Next.js Command Menu for the App Router

Add a Next.js command palette with cmdk. Drivn ships a single-file Command component with a built-in command-K dialog for any App Router client island.

A command palette is the keyboard-first menu that opens with command-K — type to filter, arrow to navigate, Enter to run. It has become a default affordance in developer tools, dashboards, and docs sites, and building one well in Next.js means handling fuzzy search, keyboard events, and a focus-trapped dialog without dropping a heavy dependency into every route.

Drivn's Command component wraps cmdk — the same proven search-list primitive shadcn uses — in a single compound component you own. After npx drivn add command the source lives in src/components/ui/command.tsx, marked 'use client', with cmdk and lucide-react as its only runtime dependencies. Dot notation exposes every piece through one import: Command.Input, Command.List, Command.Group, Command.Item, Command.Separator, Command.Empty, Command.Shortcut, and Command.Dialog. The command-K toggle, the search filtering, and the dialog wrapper are all wired inside the file.

This guide walks through installing Drivn in a Next.js 16 project, rendering an inline command menu as a client island, building the command-K palette with Command.Dialog, and grouping items with separators and shortcut hints. Every snippet is drawn from the component's actual API. For the full reference see the Command docs; for the shadcn/ui comparison see Drivn vs shadcn/ui Command.

Install in a Next.js 16 project

Drivn installs through a small CLI that writes the component source directly into your repository — there is no runtime npm package to version-lock. Open a terminal at the root of your Next.js 16 project and run npx drivn add command. The CLI prompts once for your install directory (defaulting to src/components/ui/), copies command.tsx, and resolves its dependencies: it adds cmdk to your package.json for the search list and writes the Dialog component the palette composes, since Command.Dialog renders inside Dialog.Content. The CLI reference documents every flag, including targeting a custom path or installing several components at once. After install you own the file — future Drivn releases will not overwrite it, and you can edit the internals without forking a package. Commit it to keep a clean baseline before customizing.

1# from the root of your Next.js 16 project
2npx drivn add command

Render an inline command menu as a client island

An App Router page is a server component by default, so it cannot call hooks. Drivn's Command is marked 'use client' because it wraps cmdk, which tracks the search input and selection on the client. The clean pattern is to keep the menu in a small client component and render that island inside an otherwise server-rendered page. Import Command from @/components/ui/command, then compose Command.Input, Command.List, Command.Empty, Command.Group, and Command.Item. cmdk filters the items against the input value automatically — no manual filter callback. The installation guide covers project bootstrapping; the Command docs list every prop on each subcomponent.

1'use client'
2import { Command } from '@/components/ui/command'
3import { Calendar, Search } from 'lucide-react'
4
5export function Menu() {
6 return (
7 <Command>
8 <Command.Input placeholder="Type a command..." />
9 <Command.List>
10 <Command.Empty />
11 <Command.Group heading="Suggestions">
12 <Command.Item>
13 <Calendar className="w-4 h-4" />
14 Calendar
15 </Command.Item>
16 <Command.Item>
17 <Search className="w-4 h-4" />
18 Search
19 </Command.Item>
20 </Command.Group>
21 </Command.List>
22 </Command>
23 )
24}

Build a command-K palette with Command.Dialog

The signature command-palette behavior — press command-K anywhere to open a centered, focus-trapped menu — is built into the Command.Dialog sub-component. Hold an open boolean in useState and pass open and onOpenChange; the dialog renders the cmdk menu inside Dialog.Content with p-0 max-w-lg rounded-[10px]. The global shortcut is registered inside the component source: a useEffect adds a keydown listener that toggles open when the user presses k with either the meta key (macOS) or the control key (Windows and Linux). The full code lives in the Command docs, and the Dialog component supplies the overlay and focus trap. The listener is verbatim from the component source below.

1// keydown listener, verbatim from src/components/ui/command.tsx
2React.useEffect(() => {
3 const onKeyDown = (e: KeyboardEvent) => {
4 if (e.key === 'k' && (e.metaKey || e.ctrlKey)) {
5 e.preventDefault()
6 onOpenChange(!open)
7 }
8 }
9 document.addEventListener('keydown', onKeyDown)
10 return () => {
11 document.removeEventListener('keydown', onKeyDown)
12 }
13}, [open, onOpenChange])

Group items with separators and shortcut hints

Wrap related items in Command.Group with a heading prop to render a category label, and drop Command.Separator between groups for a thin divider. The heading styling comes from arbitrary selectors in the styles object — [&_[cmdk-group-heading]]:text-xs and [&_[cmdk-group-heading]]:text-muted-foreground target the cmdk-group-heading data attribute cmdk emits, so you never wire the heading element directly. Add a Command.Shortcut inside an item to show its key hint; it renders a <kbd> with ml-auto text-xs tracking-widest text-muted-foreground, pushing the hint to the right edge of the row. The highlighted item uses data-[selected=true]:bg-accent from the styles object, so cmdk's keyboard navigation paints the active row as you arrow through it. See the Command examples for the full set of patterns.

1<Command.Dialog open={open} onOpenChange={setOpen} label="Command Menu">
2 <Command.Input placeholder="Search..." />
3 <Command.List>
4 <Command.Empty />
5 <Command.Group heading="Suggestions">
6 <Command.Item>
7 <Calendar className="w-4 h-4" />
8 Calendar
9 <Command.Shortcut>&#8984;C</Command.Shortcut>
10 </Command.Item>
11 <Command.Item>
12 <Search className="w-4 h-4" />
13 Search
14 <Command.Shortcut>&#8984;K</Command.Shortcut>
15 </Command.Item>
16 </Command.Group>
17 <Command.Separator />
18 <Command.Group heading="Settings">
19 <Command.Item>
20 <Settings className="w-4 h-4" />
21 Preferences
22 <Command.Shortcut>&#8984;S</Command.Shortcut>
23 </Command.Item>
24 </Command.Group>
25 </Command.List>
26</Command.Dialog>
Get started

Install Drivn in one command

Copy the source into your project and own every line. Zero runtime dependencies, pure React + Tailwind.

npx drivn@latest create

Requires Node 18+. Works with npm, pnpm, and yarn.

Enjoying Drivn?
Star the repo on GitHub to follow new component releases.
Star →

Frequently asked questions

The Command itself is a client component — it is marked 'use client' because it wraps cmdk, which manages the search input and selection with hooks. Your surrounding page and layout still render on the server. The usual pattern is a small client component holding the menu plus its state, rendered as an island inside an otherwise server-rendered route. Next.js inserts the client boundary automatically at the import.

Inside the Command.Dialog source, a useEffect adds a keydown listener to document that checks for e.key === 'k' together with e.metaKey || e.ctrlKey, calls e.preventDefault(), and toggles the open state. The effect returns a cleanup function that removes the listener on unmount. Because it lives in the component file you own, you can change the trigger key or add more shortcuts without patching a dependency.

No. The command-K palette renders inside Drivn's own Dialog component, which provides the overlay, focus trap, and Escape-to-close with plain React — no Radix, no floating-ui. The search list comes from cmdk, the same primitive shadcn uses. The only runtime dependencies the Command adds are cmdk and lucide-react for the search icon, plus the Dialog source the CLI writes alongside it.

Place a Command.Shortcut inside any Command.Item. It renders a <kbd> element styled with ml-auto text-xs tracking-widest text-muted-foreground, which pushes the hint to the right edge of the row and renders it in the muted token. Pass the key combination as children — <Command.Shortcut>&#8984;K</Command.Shortcut>. The hint is presentational only; you still wire the actual shortcut behavior in your own keydown handler.