Dice UI
Components

Kbd

A keyboard key primitive for displaying keyboard shortcuts and key combinations.

Basic shortcutK
Multiple keysS
With descriptions
L
Different sizes
PPP

Installation

CLI

npx shadcn@latest add "https://diceui.com/r/kbd"

Manual

Install the following dependencies:

npm install @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 RootSlot = asChild ? Slot : "kbd";
 
    return (
      <RootSlot
        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 KeySlot = asChild ? Slot : "span";
 
    const keyText = children?.toString() ?? "";
    const title = titleProp ?? KEY_DESCRIPTIONS[keyText] ?? keyText;
 
    return (
      <abbr title={title} className="no-underline">
        <KeySlot
          data-slot="kbd-key"
          {...keyProps}
          ref={forwardedRef}
          className={cn(
            "inline-flex items-center justify-center rounded",
            className,
          )}
        >
          {children}
        </KeySlot>
      </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 SeparatorSlot = asChild ? Slot : "span";
 
    return (
      <SeparatorSlot
        role="separator"
        aria-orientation="horizontal"
        aria-hidden="true"
        data-slot="kbd-separator"
        {...separatorProps}
        ref={forwardedRef}
        className={cn("text-foreground/70", className)}
      >
        {children}
      </SeparatorSlot>
    );
  },
);
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
NI
With descriptions
?

With Variants

DefaultK
OutlineEsc
GhostEnter
Function keys
F1F5F11
Mixed variants
F.

API Reference

Root

The main container component for keyboard shortcuts.

PropTypeDefault
size
"default" | "sm" | "lg"
"default"
variant
"default" | "outline" | "ghost"
"default"
asChild
boolean
false
Data AttributeValue
[data-slot]"kbd"

Key

The component that represents a single keyboard key.

PropTypeDefault
title
string
-
asChild
boolean
false
Data AttributeValue
[data-slot]"kbd-key"

Separator

The component that represents the separator between keyboard keys.

PropTypeDefault
asChild
boolean
false
Data AttributeValue
[data-slot]"kbd-separator"

On this page