Skip to content

Kbd

Displays a keyboard key or shortcut, inline with text or inside menus and tooltips.

SourceRegistry
⌘K

Press / to search or Esc to close.

import { Kbd, KbdGroup } from "@/components/ui/kbd"

export default function KbdDemo() {
  return (
    <div className="flex flex-col items-center gap-4">
      <KbdGroup>
        <Kbd>⌘</Kbd>
        <Kbd>K</Kbd>
      </KbdGroup>
      <p className="text-sm text-muted-foreground">
        Press <Kbd>/</Kbd> to search or <Kbd>Esc</Kbd> to close.
      </p>
    </div>
  )
}

Installation

$ pnpm dlx shadcn@latest add https://www.prfct.dev/r/kbd.json

Usage

import { Kbd, KbdGroup } from "@/components/ui/kbd"
<KbdGroup>
  <Kbd>⌘</Kbd>
  <Kbd>K</Kbd>
</KbdGroup>

Examples

Shortcut reference

KbdGroup sets the keys of one combination side by side. Put each physical key in its own Kbd, in the order people press them.

Command palette
⌘K
Toggle sidebar
⌘B
Find in project
⌘⇧F
Go to line
CtrlG
Delete line
⌘⌫
import { Kbd, KbdGroup } from "@/components/ui/kbd"

const shortcuts = [
  { action: "Command palette", keys: ["⌘", "K"] },
  { action: "Toggle sidebar", keys: ["⌘", "B"] },
  { action: "Find in project", keys: ["⌘", "⇧", "F"] },
  { action: "Go to line", keys: ["Ctrl", "G"] },
  { action: "Delete line", keys: ["⌘", "⌫"] },
]

export default function KbdGroupExample() {
  return (
    <dl className="grid w-full max-w-sm grid-cols-[1fr_auto] gap-x-6 gap-y-3 text-sm">
      {shortcuts.map((shortcut) => (
        <div key={shortcut.action} className="contents">
          <dt className="text-muted-foreground">{shortcut.action}</dt>
          <dd>
            <KbdGroup>
              {shortcut.keys.map((key) => (
                <Kbd key={key}>{key}</Kbd>
              ))}
            </KbdGroup>
          </dd>
        </div>
      ))}
    </dl>
  )
}

In tooltips

Teach shortcuts where people already look: in the tooltip of the button that has one. Inside a tooltip, keys switch to a translucent style that reads on the inverted surface. Declare the shortcut on the control with aria-keyshortcuts too.

import { BoldIcon, ItalicIcon, UnderlineIcon } from "lucide-react"

import { Button } from "@/components/ui/button"
import { Kbd, KbdGroup } from "@/components/ui/kbd"
import {
  Tooltip,
  TooltipContent,
  TooltipTrigger,
} from "@/components/ui/tooltip"

const tools = [
  { label: "Bold", icon: BoldIcon, key: "B" },
  { label: "Italic", icon: ItalicIcon, key: "I" },
  { label: "Underline", icon: UnderlineIcon, key: "U" },
]

export default function KbdInTooltip() {
  return (
    <div className="flex items-center gap-1 rounded-lg border bg-card p-1 shadow-raised">
      {tools.map(({ label, icon: Icon, key }) => (
        <Tooltip key={label}>
          <TooltipTrigger
            render={
              <Button
                variant="ghost"
                size="icon-sm"
                aria-label={label}
                aria-keyshortcuts={`Meta+${key}`}
              />
            }
          >
            <Icon />
          </TooltipTrigger>
          <TooltipContent>
            {label}
            <KbdGroup>
              <Kbd>⌘</Kbd>
              <Kbd>{key}</Kbd>
            </KbdGroup>
          </TooltipContent>
        </Tooltip>
      ))}
    </div>
  )
}

In menus

Menus list shortcuts in DropdownMenuShortcut, aligned to the end of each item.

import { Button } from "@/components/ui/button"
import {
  DropdownMenu,
  DropdownMenuContent,
  DropdownMenuGroup,
  DropdownMenuItem,
  DropdownMenuSeparator,
  DropdownMenuShortcut,
  DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu"
import { Kbd, KbdGroup } from "@/components/ui/kbd"

const items = [
  { label: "New file", keys: ["⌘", "N"] },
  { label: "Open…", keys: ["⌘", "O"] },
  { label: "Save", keys: ["⌘", "S"] },
  { label: "Save as…", keys: ["⌘", "⇧", "S"] },
]

export default function KbdInMenu() {
  return (
    <DropdownMenu>
      <DropdownMenuTrigger render={<Button variant="outline" />}>
        File
      </DropdownMenuTrigger>
      <DropdownMenuContent className="w-56">
        <DropdownMenuGroup>
          {items.map((item) => (
            <DropdownMenuItem key={item.label}>
              {item.label}
              <DropdownMenuShortcut>
                <KbdGroup>
                  {item.keys.map((key) => (
                    <Kbd key={key} className="bg-transparent">
                      {key}
                    </Kbd>
                  ))}
                </KbdGroup>
              </DropdownMenuShortcut>
            </DropdownMenuItem>
          ))}
        </DropdownMenuGroup>
        <DropdownMenuSeparator />
        <DropdownMenuGroup>
          <DropdownMenuItem>
            Close window
            <DropdownMenuShortcut>
              <KbdGroup>
                <Kbd className="bg-transparent">⌘</Kbd>
                <Kbd className="bg-transparent">W</Kbd>
              </KbdGroup>
            </DropdownMenuShortcut>
          </DropdownMenuItem>
        </DropdownMenuGroup>
      </DropdownMenuContent>
    </DropdownMenu>
  )
}

Platform-aware

Show the modifier people actually have: ⌘ on macOS and iOS, Ctrl everywhere else. Detect the platform on the client and render a neutral default on the server to avoid a hydration mismatch.

Search with CtrlK

Showing Windows and Linux keys for this device.

"use client"

import * as React from "react"

import { Kbd, KbdGroup } from "@/components/ui/kbd"

function subscribe() {
  return () => {}
}

/** `true` on Apple platforms. Server-renders as `false` to avoid a mismatch. */
function useIsApple() {
  return React.useSyncExternalStore(
    subscribe,
    () => /Mac|iPhone|iPad/.test(navigator.platform || navigator.userAgent),
    () => false
  )
}

export default function KbdPlatform() {
  const apple = useIsApple()
  const mod = apple ? "⌘" : "Ctrl"

  return (
    <div className="flex flex-col items-center gap-3 text-sm">
      <p className="text-muted-foreground">
        Search with{" "}
        <KbdGroup>
          <Kbd>{mod}</Kbd>
          <Kbd>K</Kbd>
        </KbdGroup>
      </p>
      <p className="text-xs text-muted-foreground">
        Showing {apple ? "macOS" : "Windows and Linux"} keys for this device.
      </p>
    </div>
  )
}
function useIsApple() {
  return React.useSyncExternalStore(
    () => () => {},
    () => /Mac|iPhone|iPad/.test(navigator.platform || navigator.userAgent),
    () => false
  )
}

In text

Keys sit on the baseline of running text, so instructions can name them in place.

Select a row and press Enter to open it. Use Shift↑ and Shift↓ to extend the selection, and Esc to clear it.

import { Kbd, KbdGroup } from "@/components/ui/kbd"

export default function KbdInText() {
  return (
    <div className="max-w-md rounded-xl border bg-card p-5 text-sm leading-6 text-muted-foreground shadow-raised">
      <p>
        Select a row and press <Kbd>Enter</Kbd> to open it. Use{" "}
        <KbdGroup>
          <Kbd>Shift</Kbd>
          <Kbd>↑</Kbd>
        </KbdGroup>{" "}
        and{" "}
        <KbdGroup>
          <Kbd>Shift</Kbd>
          <Kbd>↓</Kbd>
        </KbdGroup>{" "}
        to extend the selection, and <Kbd>Esc</Kbd> to clear it.
      </p>
    </div>
  )
}

Guidelines

When to use

  • To show a keyboard shortcut next to the action it triggers.
  • To reference a key in instructions: Press Esc to close.

When not to use

  • For code or commands — use inline code.
  • For buttons people should click — a key cap looks pressable but isn't.

Symbols and names

Use the symbols printed on the keys where they are unambiguous, and words where they aren't.

KeyWrite
Command, Option, Shift, Control (macOS)⌘ ⌥ ⇧ ⌃
Control, Alt (Windows, Linux)Ctrl, Alt
Return, Delete, Escape↵ or Enter, ⌫, Esc
Arrows↑ ↓ ← →
LettersUppercase: K, not k
⌘⇧P
DoOne key per cap, in pressing order, with the platform's modifier.
cmd+shift+p
Don’tA whole combination in one cap, or a mix of symbols and names, is harder to read.

Accessibility

  • Kbd is semantic. It renders <kbd>, which tells assistive technology the text is keyboard input. Symbols like ⌘ are announced by most screen readers ("command"); when in doubt, add a visually hidden name.
  • Declare shortcuts on controls. A visible hint helps sighted people; aria-keyshortcuts="Meta+K" on the control tells everyone else.
  • Don't hijack the platform. Avoid single-letter shortcuts that fire while typing, and never override browser or screen-reader shortcuts.
  • Contrast. Key text uses muted-foreground, which meets 4.5:1 on the key's surface in both modes.

API reference

Kbd

Renders a <kbd> element and accepts all of its props. Icons inside are sized to 12px.

KbdGroup

Renders a <kbd> that lays out several Kbd elements with a 4px gap — nested <kbd> elements are the HTML way to describe a key combination.