Kbd
Displays a keyboard key or shortcut, inline with text or inside menus and tooltips.
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.jsonUsage
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.
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.