Kbd
A component for displaying keyboard keys and shortcuts. Built with Base UI and Tailwind CSS. Copy-paste ready.
Single keys:
Key combinations:
import { Kbd, KbdGroup } from "@/components/ui/kbd";
export default function Particle() {
return (
<div className="flex flex-col gap-4">
<div>
<p className="mb-2 text-muted-foreground text-sm">Single keys:</p>
<div className="flex gap-2">
<Kbd>K</Kbd>
<Kbd>⌘</Kbd>
<Kbd>⌃</Kbd>
<Kbd>⇧</Kbd>
</div>
</div>
<div>
<p className="mb-2 text-muted-foreground text-sm">Key combinations:</p>
<div className="flex gap-2">
<KbdGroup>
<Kbd>⌘</Kbd>
<Kbd>K</Kbd>
</KbdGroup>
<KbdGroup>
<Kbd>⌘</Kbd>
<Kbd>Shift</Kbd>
<Kbd>P</Kbd>
</KbdGroup>
<KbdGroup>
<Kbd>Ctrl</Kbd>
<Kbd>Alt</Kbd>
<Kbd>Delete</Kbd>
</KbdGroup>
</div>
</div>
</div>
);
}
Installation
pnpm dlx shadcn@latest add @cnippet/kbd
Usage
import { Kbd, KbdGroup } from "@/components/ui/kbd"Single key:
<Kbd>K</Kbd>Multiple keys (key combination):
<KbdGroup>
<Kbd>⌘</Kbd>
<Kbd>K</Kbd>
</KbdGroup>Examples
Input group
A search input with a KbdGroup addon showing the keyboard shortcut to focus the field — common in command bars.
import {
InputGroup,
InputGroupAddon,
InputGroupInput,
} from "@/components/ui/input-group";
import { Kbd } from "@/components/ui/kbd";
export default function Particle() {
return (
<InputGroup>
<InputGroupInput placeholder="Search…" type="search" />
<InputGroupAddon align="inline-end">
<Kbd>⌘K</Kbd>
</InputGroupAddon>
</InputGroup>
);
}
Keys grouped together
Use KbdGroup to wrap multiple Kbd elements that form a chord, visually connecting them as a single shortcut.
import { Kbd, KbdGroup } from "@/components/ui/kbd";
export function Pattern() {
return (
<div className="flex items-center justify-center">
<KbdGroup>
<Kbd>Ctrl</Kbd>
<Kbd>Shift</Kbd>
<Kbd>P</Kbd>
</KbdGroup>
</div>
);
}
With icons
Replace text key labels with icon symbols (⌘, ⇧, ⌥) to match platform conventions on macOS.
import { ArrowLeftIcon, CircleDashedIcon } from "lucide-react";
import { Kbd, KbdGroup } from "@/components/ui/kbd";
export function Pattern() {
return (
<div className="flex items-center justify-center">
<KbdGroup>
<Kbd>
<ArrowLeftIcon />
Left
</Kbd>
<Kbd>
<CircleDashedIcon />
Voice Enabled
</Kbd>
</KbdGroup>
</div>
);
}
In a tooltip
Append shortcut keys inside a TooltipContent so users can discover keyboard shortcuts on hover without cluttering the UI.
import { SaveIcon } from "lucide-react";
import { Button } from "@/components/ui/button";
import { Kbd } from "@/components/ui/kbd";
import {
Tooltip,
TooltipContent,
TooltipTrigger,
} from "@/components/ui/tooltip";
export function Pattern() {
return (
<div className="flex items-center justify-center">
<Tooltip>
<TooltipTrigger render={<Button size="icon-sm" variant="outline" />}>
<SaveIcon />
</TooltipTrigger>
<TooltipContent className="pr-1.5">
<div className="flex items-center gap-2">
Save Changes <Kbd>S</Kbd>
</div>
</TooltipContent>
</Tooltip>
</div>
);
}
Reference list
A formatted keyboard shortcut reference card listing actions and their corresponding key combinations in a two-column layout.
Keyboard Shortcuts
import { Kbd, KbdGroup } from "@/components/ui/kbd";
import { Separator } from "@/components/ui/separator";
const shortcuts = [
{ keys: ["⌘", "K"], label: "Search" },
{ keys: ["⌘", "N"], label: "New File" },
{ keys: ["⌘", "S"], label: "Save" },
{ keys: ["⌘", "Z"], label: "Undo" },
{ keys: ["⌘", "⇧", "Z"], label: "Redo" },
];
export function Pattern() {
return (
<div className="mx-auto flex flex-col">
<p className="mb-3 font-medium text-sm">Keyboard Shortcuts</p>
<Separator />
<div className="flex flex-col">
{shortcuts.map((shortcut) => (
<div
className="flex items-center justify-between border-b py-2.5 last:border-b-0"
key={shortcut.label}
>
<span className="text-muted-foreground text-sm">
{shortcut.label}
</span>
<KbdGroup>
{shortcut.keys.map((key) => (
<Kbd key={key}>{key}</Kbd>
))}
</KbdGroup>
</div>
))}
</div>
</div>
);
}
In Buttons
KbdGroup appended inside Button components to surface keyboard shortcuts inline with the action they trigger.
import { PrinterIcon, SaveIcon } from "lucide-react";
import { Button } from "@/components/ui/button";
import { Kbd, KbdGroup } from "@/components/ui/kbd";
export default function Particle() {
return (
<div className="flex flex-wrap gap-2">
<Button variant="outline">
<SaveIcon aria-hidden="true" />
Save
<KbdGroup className="-me-1">
<Kbd>⌘</Kbd>
<Kbd>S</Kbd>
</KbdGroup>
</Button>
<Button variant="outline">
<PrinterIcon aria-hidden="true" />
Print
<KbdGroup className="-me-1">
<Kbd>⌘</Kbd>
<Kbd>P</Kbd>
</KbdGroup>
</Button>
</div>
);
}
Arrow Keys
A directional pad layout of arrow key Kbd badges with labels below — useful in onboarding flows or keyboard shortcut docs.
Navigate with arrow keys
import { Kbd } from "@/components/ui/kbd";
const arrowKeys = [
{ key: "↑", label: "Up" },
{ key: "↓", label: "Down" },
{ key: "←", label: "Left" },
{ key: "→", label: "Right" },
];
export function Pattern() {
return (
<div className="flex flex-col items-center gap-3">
<p className="text-muted-foreground text-xs">Navigate with arrow keys</p>
<div className="flex flex-col items-center gap-1">
<Kbd aria-label="Up arrow">↑</Kbd>
<div className="flex gap-1">
<Kbd aria-label="Left arrow">←</Kbd>
<Kbd aria-label="Down arrow">↓</Kbd>
<Kbd aria-label="Right arrow">→</Kbd>
</div>
</div>
<div className="flex gap-3">
{arrowKeys.map(({ key, label }) => (
<div className="flex flex-col items-center gap-1" key={label}>
<Kbd aria-label={`${label} arrow`}>{key}</Kbd>
<span className="text-muted-foreground text-xs">{label}</span>
</div>
))}
</div>
</div>
);
}
Cross-Platform Shortcuts
A three-column table comparing action labels with Mac (⌘) and Windows/Linux (Ctrl) equivalents side by side.
import { EditIcon, SearchIcon, UndoIcon } from "lucide-react";
import { Fragment } from "react";
import { Kbd, KbdGroup } from "@/components/ui/kbd";
import { Separator } from "@/components/ui/separator";
const shortcuts = [
{
icon: SearchIcon,
label: "Open command palette",
mac: ["⌘", "K"],
win: ["Ctrl", "K"],
},
{
icon: EditIcon,
label: "Find and replace",
mac: ["⌘", "H"],
win: ["Ctrl", "H"],
},
{
icon: UndoIcon,
label: "Undo last action",
mac: ["⌘", "Z"],
win: ["Ctrl", "Z"],
},
];
export function Pattern() {
return (
<div className="w-full max-w-sm">
<div className="grid grid-cols-[1fr_auto_auto] items-center gap-x-4 gap-y-0">
<span className="pb-2 font-medium text-muted-foreground text-xs">
Action
</span>
<span className="pb-2 font-medium text-muted-foreground text-xs">
Mac
</span>
<span className="pb-2 font-medium text-muted-foreground text-xs">
Win / Linux
</span>
<Separator className="col-span-3" />
{shortcuts.map(({ icon: Icon, label, mac, win }) => (
<Fragment key={label}>
<div className="flex items-center gap-2 py-2.5">
<Icon
aria-hidden="true"
className="size-3.5 text-muted-foreground"
/>
<span className="text-sm">{label}</span>
</div>
<KbdGroup className="py-2.5">
{mac.map((k) => (
<Kbd key={k}>{k}</Kbd>
))}
</KbdGroup>
<KbdGroup className="py-2.5">
{win.map((k) => (
<Kbd key={k}>{k}</Kbd>
))}
</KbdGroup>
</Fragment>
))}
</div>
</div>
);
}
In Menu Items
Shortcut badges aligned to the trailing edge of MenuItem components using ms-auto — the standard pattern for context menus.
import { CopyIcon, DownloadIcon, PencilIcon, TrashIcon } from "lucide-react";
import { Button } from "@/components/ui/button";
import { Kbd, KbdGroup } from "@/components/ui/kbd";
import {
Menu,
MenuItem,
MenuPopup,
MenuTrigger,
} from "@/components/ui/menu";
export function Pattern() {
return (
<Menu>
<MenuTrigger render={<Button variant="outline" />}>
Edit document
</MenuTrigger>
<MenuPopup>
<MenuItem>
<PencilIcon aria-hidden="true" />
Rename
<KbdGroup className="ms-auto">
<Kbd>⌘</Kbd>
<Kbd>R</Kbd>
</KbdGroup>
</MenuItem>
<MenuItem>
<CopyIcon aria-hidden="true" />
Duplicate
<KbdGroup className="ms-auto">
<Kbd>⌘</Kbd>
<Kbd>D</Kbd>
</KbdGroup>
</MenuItem>
<MenuItem>
<DownloadIcon aria-hidden="true" />
Export
<KbdGroup className="ms-auto">
<Kbd>⌘</Kbd>
<Kbd>E</Kbd>
</KbdGroup>
</MenuItem>
<MenuItem variant="destructive">
<TrashIcon aria-hidden="true" />
Delete
<Kbd className="ms-auto">⌫</Kbd>
</MenuItem>
</MenuPopup>
</Menu>
);
}
Command Palette Results
A command palette result list where each row includes a trailing Kbd for the confirmation key, plus a footer legend for navigate, open, and close.
Recent
- Getting started guideUpdated 2 hours ago↵
- Design resources12 documents↵
- Account settingsWorkspace · Billing↵
import { FileTextIcon, FolderIcon, SettingsIcon } from "lucide-react";
import { Kbd, KbdGroup } from "@/components/ui/kbd";
const results = [
{
description: "Updated 2 hours ago",
icon: FileTextIcon,
kbd: ["↵"],
label: "Getting started guide",
},
{
description: "12 documents",
icon: FolderIcon,
kbd: ["↵"],
label: "Design resources",
},
{
description: "Workspace · Billing",
icon: SettingsIcon,
kbd: ["↵"],
label: "Account settings",
},
];
export function Pattern() {
return (
<div className="w-full max-w-sm overflow-hidden rounded-lg border bg-popover shadow-md">
<div className="border-b px-3 py-2">
<p className="text-muted-foreground text-xs">Recent</p>
</div>
<ul>
{results.map(({ description, icon: Icon, kbd, label }, i) => (
<li
className={`flex cursor-pointer items-center gap-3 px-3 py-2.5 text-sm transition-colors hover:bg-accent ${i === 0 ? "bg-accent" : ""}`}
key={label}
>
<Icon
aria-hidden="true"
className="size-4 shrink-0 text-muted-foreground"
/>
<div className="flex min-w-0 flex-1 flex-col">
<span className="truncate font-medium">{label}</span>
<span className="truncate text-muted-foreground text-xs">
{description}
</span>
</div>
<KbdGroup className="shrink-0">
{kbd.map((k) => (
<Kbd key={k}>{k}</Kbd>
))}
</KbdGroup>
</li>
))}
</ul>
<div className="flex items-center gap-3 border-t px-3 py-2">
<span className="flex items-center gap-1 text-muted-foreground text-xs">
<KbdGroup>
<Kbd>↑</Kbd>
<Kbd>↓</Kbd>
</KbdGroup>{" "}
navigate
</span>
<span className="flex items-center gap-1 text-muted-foreground text-xs">
<Kbd>↵</Kbd> open
</span>
<span className="flex items-center gap-1 text-muted-foreground text-xs">
<Kbd>Esc</Kbd> close
</span>
</div>
</div>
);
}

