Components
Basic shortcut⌘K
Multiple keys⌘⇧S
With descriptions
⊞L⌥⌫
Different sizes
⌘P⌘P⌘P
import * as Kbd from "@/components/ui/kbd";
export function KbdDemo() {
return (
<div className="flex flex-col gap-6">
<div className="flex flex-col gap-2">
<span className="text-muted-foreground text-sm">Basic shortcut</span>
<Kbd.Root>
<Kbd.Key>⌘</Kbd.Key>
<Kbd.Separator />
<Kbd.Key title="Search">K</Kbd.Key>
</Kbd.Root>
</div>
<div className="flex flex-col gap-2">
<span className="text-muted-foreground text-sm">Multiple keys</span>
<Kbd.Root>
<Kbd.Key>⌘</Kbd.Key>
<Kbd.Separator />
<Kbd.Key>⇧</Kbd.Key>
<Kbd.Separator />
<Kbd.Key title="Save">S</Kbd.Key>
</Kbd.Root>
</div>
<div className="flex flex-col gap-2">
<span className="text-muted-foreground text-sm">With descriptions</span>
<div className="flex items-center gap-4">
<Kbd.Root>
<Kbd.Key title="Windows key">⊞</Kbd.Key>
<Kbd.Separator />
<Kbd.Key title="Lock screen">L</Kbd.Key>
</Kbd.Root>
<Kbd.Root>
<Kbd.Key title="Option">⌥</Kbd.Key>
<Kbd.Separator />
<Kbd.Key title="Delete word">⌫</Kbd.Key>
</Kbd.Root>
</div>
</div>
<div className="flex flex-col gap-2">
<span className="text-muted-foreground text-sm">Different sizes</span>
<div className="flex items-end gap-4">
<Kbd.Root size="sm">
<Kbd.Key>⌘</Kbd.Key>
<Kbd.Separator />
<Kbd.Key>P</Kbd.Key>
</Kbd.Root>
<Kbd.Root>
<Kbd.Key>⌘</Kbd.Key>
<Kbd.Separator />
<Kbd.Key>P</Kbd.Key>
</Kbd.Root>
<Kbd.Root size="lg">
<Kbd.Key>⌘</Kbd.Key>
<Kbd.Separator />
<Kbd.Key>P</Kbd.Key>
</Kbd.Root>
</div>
</div>
</div>
);
}
Installation
CLI
npx shadcn@latest add "https://diceui.com/r/kbd"
pnpm dlx shadcn@latest add "https://diceui.com/r/kbd"
yarn dlx shadcn@latest add "https://diceui.com/r/kbd"
bun x shadcn@latest add "https://diceui.com/r/kbd"
Manual
Install the following dependencies:
npm install @radix-ui/react-slot
pnpm add @radix-ui/react-slot
yarn add @radix-ui/react-slot
bun add @radix-ui/react-slot
Copy and paste the following code into your project.
import { cn } from "@/lib/utils";
import { Slot } from "@radix-ui/react-slot";
import { type VariantProps, cva } from "class-variance-authority";
import * as React from "react";
const kbdVariants = cva(
"inline-flex w-fit items-center gap-1 font-medium font-mono text-[10px] text-foreground/70 sm:text-[11px]",
{
variants: {
size: {
default: "h-6 rounded px-1.5",
sm: "h-5 rounded-sm px-1",
lg: "h-7 rounded-md px-2",
},
variant: {
default: "bg-accent",
outline:
"bg-background px-0 [&_[data-slot='kbd-key']]:min-w-[20px] [&_[data-slot='kbd-key']]:border [&_[data-slot='kbd-key']]:border-border [&_[data-slot='kbd-key']]:bg-muted/30 [&_[data-slot='kbd-key']]:px-1.5 [&_[data-slot='kbd-key']]:shadow-xs",
ghost: "bg-transparent shadow-none",
},
},
defaultVariants: {
size: "default",
variant: "default",
},
},
);
interface KbdRootProps
extends React.ComponentPropsWithoutRef<"kbd">,
VariantProps<typeof kbdVariants> {
asChild?: boolean;
}
const KbdRoot = React.forwardRef<HTMLElement, KbdRootProps>(
(props, forwardedRef) => {
const {
variant = "default",
size = "default",
asChild,
className,
...rootProps
} = props;
const RootPrimitive = asChild ? Slot : "kbd";
return (
<RootPrimitive
role="group"
data-slot="kbd"
{...rootProps}
ref={forwardedRef}
className={cn(kbdVariants({ size, variant, className }))}
/>
);
},
);
KbdRoot.displayName = "KbdRoot";
const KEY_DESCRIPTIONS: Record<string, string> = {
"⌘": "Command",
"⇧": "Shift",
"⌥": "Option",
"⌃": "Control",
Ctrl: "Control",
"⌫": "Backspace",
"⎋": "Escape",
"↩": "Return",
"⇥": "Tab",
"⌤": "Enter",
"↑": "Arrow Up",
"↓": "Arrow Down",
"←": "Arrow Left",
"→": "Arrow Right",
"⇪": "Caps Lock",
fn: "Function",
"⌦": "Delete",
"⇞": "Page Up",
"⇟": "Page Down",
"↖": "Home",
"↘": "End",
"↕": "Page Up/Down",
"↔": "Left/Right",
} as const;
interface KbdKeyProps extends React.ComponentPropsWithoutRef<"span"> {
asChild?: boolean;
}
const KbdKey = React.forwardRef<HTMLSpanElement, KbdKeyProps>(
(props, forwardedRef) => {
const {
asChild,
className,
children,
title: titleProp,
...keyProps
} = props;
const keyText = children?.toString() ?? "";
const title = titleProp ?? KEY_DESCRIPTIONS[keyText] ?? keyText;
const KeyPrimitive = asChild ? Slot : "span";
return (
<abbr title={title} className="no-underline">
<KeyPrimitive
data-slot="kbd-key"
{...keyProps}
ref={forwardedRef}
className={cn(
"inline-flex items-center justify-center rounded",
className,
)}
>
{children}
</KeyPrimitive>
</abbr>
);
},
);
KbdKey.displayName = "KbdKey";
interface KbdSeparatorProps extends React.ComponentPropsWithoutRef<"span"> {
asChild?: boolean;
}
const KbdSeparator = React.forwardRef<HTMLSpanElement, KbdSeparatorProps>(
(props, forwardedRef) => {
const { asChild, children = "+", className, ...separatorProps } = props;
const SeparatorPrimitive = asChild ? Slot : "span";
return (
<SeparatorPrimitive
role="separator"
aria-orientation="horizontal"
aria-hidden="true"
data-slot="kbd-separator"
{...separatorProps}
ref={forwardedRef}
className={cn("text-foreground/70", className)}
>
{children}
</SeparatorPrimitive>
);
},
);
KbdSeparator.displayName = "KbdSeparator";
const Kbd = KbdRoot;
const Root = KbdRoot;
const Key = KbdKey;
const Separator = KbdSeparator;
export {
Kbd,
KbdKey,
KbdSeparator,
//
Root,
Key,
Separator,
};
Layout
Import the parts, and compose them together.
import * as Kbd from "@/components/ui/kbd";
<Kbd.Root>
<Kbd.Key/>
<Kbd.Separator />
<Kbd.Key />
</Kbd.Root>
Built-in Key Titles
The component includes built-in titles for common keyboard symbols:
<Kbd.Root>
<Kbd.Key>⌘</Kbd.Key> {/* Shows "Command" on hover */}
<Kbd.Key>⇧</Kbd.Key> {/* Shows "Shift" on hover */}
<Kbd.Key>⌥</Kbd.Key> {/* Shows "Option" on hover */}
</Kbd.Root>
Custom Key Titles
You can provide custom titles for any key:
<Kbd.Root>
<Kbd.Key title="Windows key">⊞</Kbd.Key>
<Kbd.Separator />
<Kbd.Key title="Lock screen">L</Kbd.Key>
</Kbd.Root>
Examples
With Multiple Keys
IDE shortcuts
⌘⇧F⌘⌥↓
System shortcuts
⌘⌥⎋⌘⇧⌥P
Browser shortcuts
⌘⇧N⌘⌥I
With descriptions
⌃⌥⌫⌘⇧?
import * as Kbd from "@/components/ui/kbd";
export function KbdMultipleDemo() {
return (
<div className="grid gap-6 sm:grid-cols-2">
<div className="flex flex-col gap-2">
<span className="text-muted-foreground text-sm">IDE shortcuts</span>
<div className="flex flex-col gap-2">
<Kbd.Root>
<Kbd.Key>⌘</Kbd.Key>
<Kbd.Separator />
<Kbd.Key>⇧</Kbd.Key>
<Kbd.Separator />
<Kbd.Key title="Find in files">F</Kbd.Key>
</Kbd.Root>
<Kbd.Root>
<Kbd.Key>⌘</Kbd.Key>
<Kbd.Separator />
<Kbd.Key>⌥</Kbd.Key>
<Kbd.Separator />
<Kbd.Key title="Multi-cursor">↓</Kbd.Key>
</Kbd.Root>
</div>
</div>
<div className="flex flex-col gap-2">
<span className="text-muted-foreground text-sm">System shortcuts</span>
<div className="flex flex-col gap-2">
<Kbd.Root>
<Kbd.Key>⌘</Kbd.Key>
<Kbd.Separator />
<Kbd.Key>⌥</Kbd.Key>
<Kbd.Separator />
<Kbd.Key title="Force quit">⎋</Kbd.Key>
</Kbd.Root>
<Kbd.Root>
<Kbd.Key>⌘</Kbd.Key>
<Kbd.Separator />
<Kbd.Key>⇧</Kbd.Key>
<Kbd.Separator />
<Kbd.Key>⌥</Kbd.Key>
<Kbd.Separator />
<Kbd.Key title="System preferences">P</Kbd.Key>
</Kbd.Root>
</div>
</div>
<div className="flex flex-col gap-2">
<span className="text-muted-foreground text-sm">Browser shortcuts</span>
<div className="flex flex-col gap-2">
<Kbd.Root>
<Kbd.Key>⌘</Kbd.Key>
<Kbd.Separator />
<Kbd.Key>⇧</Kbd.Key>
<Kbd.Separator />
<Kbd.Key title="New incognito window">N</Kbd.Key>
</Kbd.Root>
<Kbd.Root>
<Kbd.Key>⌘</Kbd.Key>
<Kbd.Separator />
<Kbd.Key>⌥</Kbd.Key>
<Kbd.Separator />
<Kbd.Key title="Developer tools">I</Kbd.Key>
</Kbd.Root>
</div>
</div>
<div className="flex flex-col gap-2">
<span className="text-muted-foreground text-sm">With descriptions</span>
<div className="flex flex-col gap-2">
<Kbd.Root>
<Kbd.Key title="Control">⌃</Kbd.Key>
<Kbd.Separator />
<Kbd.Key title="Option">⌥</Kbd.Key>
<Kbd.Separator />
<Kbd.Key title="Delete">⌫</Kbd.Key>
</Kbd.Root>
<Kbd.Root>
<Kbd.Key title="Command">⌘</Kbd.Key>
<Kbd.Separator />
<Kbd.Key title="Shift">⇧</Kbd.Key>
<Kbd.Separator />
<Kbd.Key title="Question mark">?</Kbd.Key>
</Kbd.Root>
</div>
</div>
</div>
);
}
With Variants
Default⌘K
Outline⎋Esc
Ghost⌤Enter
Function keys
F1F5F11
Mixed variants
⌘⇧F⌥.
import * as Kbd from "@/components/ui/kbd";
export function KbdVariantsDemo() {
return (
<div className="grid gap-6 sm:grid-cols-2">
<div className="flex flex-col gap-2">
<span className="text-muted-foreground text-sm">Default</span>
<Kbd.Root>
<Kbd.Key>⌘</Kbd.Key>
<Kbd.Separator />
<Kbd.Key>K</Kbd.Key>
</Kbd.Root>
</div>
<div className="flex flex-col gap-2">
<span className="text-muted-foreground text-sm">Outline</span>
<Kbd.Root variant="outline">
<Kbd.Key>⎋</Kbd.Key>
<Kbd.Separator />
<Kbd.Key title="Escape">Esc</Kbd.Key>
</Kbd.Root>
</div>
<div className="flex flex-col gap-2">
<span className="text-muted-foreground text-sm">Ghost</span>
<Kbd.Root variant="ghost">
<Kbd.Key>⌤</Kbd.Key>
<Kbd.Separator />
<Kbd.Key title="Return">Enter</Kbd.Key>
</Kbd.Root>
</div>
<div className="flex flex-col gap-2">
<span className="text-muted-foreground text-sm">Function keys</span>
<div className="flex items-center gap-1">
<Kbd.Root variant="outline">
<Kbd.Key title="Function key 1">F1</Kbd.Key>
</Kbd.Root>
<Kbd.Root variant="outline">
<Kbd.Key title="Function key 5">F5</Kbd.Key>
</Kbd.Root>
<Kbd.Root variant="outline">
<Kbd.Key title="Function key 11">F11</Kbd.Key>
</Kbd.Root>
</div>
</div>
<div className="flex flex-col gap-2">
<span className="text-muted-foreground text-sm">Mixed variants</span>
<div className="flex items-center gap-2">
<Kbd.Root>
<Kbd.Key>⌘</Kbd.Key>
<Kbd.Separator />
<Kbd.Key>⇧</Kbd.Key>
<Kbd.Separator />
<Kbd.Key title="Find in files">F</Kbd.Key>
</Kbd.Root>
<Kbd.Root variant="outline">
<Kbd.Key>⌥</Kbd.Key>
<Kbd.Separator />
<Kbd.Key title="Quick fix">.</Kbd.Key>
</Kbd.Root>
</div>
</div>
</div>
);
}
API Reference
Root
The main container component for keyboard shortcuts.
Prop | Type | Default |
---|---|---|
asChild? | boolean | false |
variant? | "default" | "outline" | "ghost" | "default" |
size? | "default" | "sm" | "lg" | "default" |
Data Attribute | Value |
---|---|
[data-slot] | "kbd" |
Key
The component that represents a single keyboard key.
Prop | Type | Default |
---|---|---|
asChild? | boolean | false |
title? | string | - |
Data Attribute | Value |
---|---|
[data-slot] | "kbd-key" |
Separator
The component that represents the separator between keyboard keys.
Prop | Type | Default |
---|---|---|
asChild? | boolean | false |
Data Attribute | Value |
---|---|
[data-slot] | "kbd-separator" |