Skip to content
  1. Keyline UI
  2. Command menu
Keyline UIComponents
  • Get started
    • Installation
    • Theming
    • Modes
    • Configuration
    • Composition
  • Actions
    • Button
    • Button group
    • Split button
    • Toggle
    • Toggle group
    • Toolbar
    • Menu
    • Menubar
    • Context menu
  • Inputs
    • Field and form
    • Checkbox
    • Checkbox group
    • Radio group
    • Switch
    • Slider
    • Segmented
    • Select
    • Native select
    • Combobox
    • Multiselect
    • Number field
    • OTP field
    • Time field
    • Date picker and calendar
    • Date range
  • Data
    • Table
    • Chart
    • Stat and meter
    • Status and badge
    • Progress
    • Skeleton
    • Empty
    • Avatar
    • Kbd
    • Marker
  • Navigation
    • Tabs
    • Breadcrumb
    • Pagination
    • Nav
    • Navigation menu
    • Command menu
  • Overlays
    • Dialog
    • Alert dialog
    • Drawer
    • Sheet
    • Popover
    • Hover card
    • Tooltip
    • Toast
    • Notice
  • Layout
    • App shell
    • Sidebar
    • Panel
    • Resizable
    • Scroll area
    • Accordion
    • Collapsible
    • Carousel
    • Inspector
    • Item
    • Direction
  • Conversation
    • Message
    • Bubble
    • Message scroller
    • Attachment
    • Questionnaire
  • Blocks
    • Shipments

Command menu

Commands by name. Typing filters the list, and results are numbered in the order they show, so ⌥ and a number runs one without moving to it. The chosen row floods as it runs. You open it; the dialog doesn't bind ⌘K for you.

Loading the live example…

"use client"

import { useEffect, useState } from "react"
import { Button } from "@/components/keyline/ui/button"
import { CommandDialog, CommandEmpty, CommandFooter, CommandGroup, CommandInput, CommandItem, CommandList } from "@/components/keyline/ui/command"
import { Kbd, KbdGroup } from "@/components/keyline/ui/kbd"

const PAGES = ["Orders", "Clients", "Invoices", "Warehouse"]

export function OfficeCommands() {
  const [open, setOpen] = useState(false)
  const [q, setQ] = useState("")
  const [last, setLast] = useState("Nothing run yet.")

  // ⌘K or Ctrl+K opens it from anywhere on the page
  useEffect(() => {
    const onKey = (e: KeyboardEvent) => {
      if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === "k") {
        e.preventDefault()
        setOpen((o) => !o)
      }
    }
    window.addEventListener("keydown", onKey)
    return () => window.removeEventListener("keydown", onKey)
  }, [])

  const run = (said: string) => () => {
    setOpen(false)
    setQ("")
    setLast(said)
  }

  return (
    <div className="grid gap-4">
      <div className="flex flex-wrap items-center gap-3">
        <Button onClick={() => setOpen(true)}>Commands</Button>
        <span className="text-[14px] text-kl-ink-2">
          or press{" "}
          <KbdGroup>
            <Kbd>Ctrl</Kbd>
            <Kbd>K</Kbd>
          </KbdGroup>
        </span>
      </div>
      <p role="status" className="m-0 text-[14px] text-kl-ink-2">
        {last}
      </p>
      <CommandDialog open={open} onOpenChange={(o) => (setOpen(o), o || setQ(""))}>
        <CommandInput placeholder="Type a command or a client…" value={q} onValueChange={setQ} />
        <CommandList>
          <CommandEmpty query={q}>Try “order”, “call” or a client.</CommandEmpty>
          <CommandGroup heading="Go to">
            {PAGES.map((p) => (
              <CommandItem key={p} value={`go to ${p}`} onSelect={run(`Opened ${p}.`)}>
                {p}
              </CommandItem>
            ))}
          </CommandGroup>
          <CommandGroup heading="Actions">
            <CommandItem value="new order" shortcut="N" onSelect={run("Started a new order.")}>
              New order
            </CommandItem>
            <CommandItem value="call lackawanna county" meta="Dwight" onSelect={run("Calling Lackawanna County.")}>
              Call Lackawanna County
            </CommandItem>
            <CommandItem value="print invoices" onSelect={run("Sent 40 invoices to the printer.")}>
              Print today&apos;s invoices
            </CommandItem>
            <CommandItem value="order from stamford" disabled>
              Order from Stamford (closed)
            </CommandItem>
          </CommandGroup>
        </CommandList>
        <CommandFooter>
          <span>
            <Kbd>↑↓</Kbd> move
          </span>
          <span>
            <Kbd>↵</Kbd> run
          </span>
          <span>
            <Kbd>⌥1–9</Kbd> run by number
          </span>
        </CommandFooter>
      </CommandDialog>
    </div>
  )
}

Installation

npx shadcn@latest add @keyline-ui/command

Run it where your components.json is. The first time, map the @keyline-ui registry: one command, in Installation.

Usage

import { CommandDialog, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList } from "@/components/keyline/ui/command"
<CommandDialog open={open} onOpenChange={setOpen}>
  <CommandInput placeholder="Type a command or a client…" value={q} onValueChange={setQ} />
  <CommandList>
    <CommandEmpty query={q}>Try “order” or a client.</CommandEmpty>
    <CommandGroup heading="Actions">
      <CommandItem value="new order" shortcut="N" onSelect={newOrder}>New order</CommandItem>
    </CommandGroup>
  </CommandList>
</CommandDialog>

Props

PropTypeDefaultDescription
open · onOpenChangeboolean · (open) => voidDefault —On CommandDialog. Bind your own shortcut to open it.
labelstringDefault "Command menu"On CommandDialog: names the dialog.
finalFocusfalse | elementDefault the openerOn CommandDialog: return false when a command moves focus elsewhere, such as into another dialog.
valuestringDefault —On CommandItem: the words it matches, which can include more than its label.
onSelect() => voidDefault —On CommandItem: runs it. Close the dialog here.
meta · shortcutReactNode · stringDefault —On CommandItem: a note at the row's end, and its key.
querystringDefault —On CommandEmpty: quoted in the no-match line.
headingReactNodeDefault —On CommandGroup.

Filtering and keyboard are cmdk's; the dialog is Base UI's. CommandFooter holds key hints.

Sheet 47 of 73PreviousNavigation menuNextDialog
On this page