HextaUI

Kbd

Key caps for shortcuts that show the right symbols on every platform, read them out properly and press down with the real keys.

ShiftCommandP

Press Command K to search, or hold Shift and watch the keys.

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

export function KbdDemo() {
  return (
    <div className="flex flex-col items-center gap-6 text-sm text-muted-foreground">
      <KbdGroup keys="mod+shift+p" size="lg" listen />
      <p>
        Press <Kbd keys="mod+k" listen /> to search, or hold{" "}
        <Kbd keys="shift" listen /> and watch the keys.
      </p>
    </div>
  )
}
pnpm dlx shadcn@latest add https://hextaui.com/r/kbd.json

Adds the component, the HextaUI theme tokens and any HextaUI components it depends on.

import { Kbd, KbdGroup } from "@/components/ui/kbd"
<KbdGroup keys="mod+k" />
<Kbd keys="escape" />
<Kbd>K</Kbd>

Write shortcuts once with keys and they show as ⌘K on a Mac and Ctrl K on Windows and Linux. Or pass any content as children for full control.

Kbd

KbdGroup
└── Kbd

Variants

keycap has a hairline edge and a 1px lip so it reads as a physical key. flat is a quiet fill for dense places like menus.

CommandCCommandC
import { KbdGroup } from "@/components/ui/kbd"

export function KbdVariants() {
  return (
    <div className="flex flex-wrap items-center justify-center gap-6">
      <KbdGroup keys="mod+c" />
      <KbdGroup keys="mod+c" variant="flat" />
    </div>
  )
}

Sizes

sm sits in small text, default next to body text and lg in headings or on its own.

ShiftCommandZShiftCommandZShiftCommandZ
import { KbdGroup } from "@/components/ui/kbd"

const sizes = ["sm", "default", "lg"] as const

export function KbdSizes() {
  return (
    <div className="flex flex-wrap items-center justify-center gap-6">
      {sizes.map((size) => (
        <KbdGroup key={size} keys="mod+shift+z" size={size} />
      ))}
    </div>
  )
}

Shortcuts

<KbdGroup /> splits a combo into one cap per key. A space starts a sequence, joined by separator. <Kbd /> with keys keeps the whole combo in one cap.

Command palette
CommandK
Move line up
OptionUp arrow
Close
Escape
Go to dashboard
GthenD
Save, as one cap
Command S
import { Kbd, KbdGroup } from "@/components/ui/kbd"

const shortcuts = [
  { label: "Command palette", keys: "mod+k" },
  { label: "Move line up", keys: "alt+up" },
  { label: "Close", keys: "escape" },
  { label: "Go to dashboard", keys: "g d" },
]

export function KbdShortcuts() {
  return (
    <dl className="grid w-full max-w-xs grid-cols-[1fr_auto] items-center gap-x-6 gap-y-3 text-sm">
      {shortcuts.map((shortcut) => (
        <div key={shortcut.keys} className="contents">
          <dt className="text-muted-foreground">{shortcut.label}</dt>
          <dd>
            <KbdGroup keys={shortcut.keys} />
          </dd>
        </div>
      ))}
      <dt className="text-muted-foreground">Save, as one cap</dt>
      <dd>
        <Kbd keys="mod+s" />
      </dd>
    </dl>
  )
}

Live keys

With listen, a cap presses down while its real key is held. It only watches, so it never blocks, delays or changes what you type. Letters match by physical key, so Option and Shift don't confuse them.

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

const rows = ["qwertyuiop", "asdfghjkl", "zxcvbnm"]

export function KbdListen() {
  return (
    <KbdGroup listen size="lg" className="flex-col">
      {rows.map((row) => (
        <span key={row} className="flex gap-1">
          {row.split("").map((key) => (
            <Kbd key={key}>{key.toUpperCase()}</Kbd>
          ))}
        </span>
      ))}
      <span className="flex gap-1">
        <Kbd keys="shift" />
        <Kbd keys="space" className="min-w-40" />
        <Kbd keys="enter" />
      </span>
    </KbdGroup>
  )
}

In a button

Inside a button, the cap takes its colors from the button's text, so it fits every variant.

import { IconSearch } from "@tabler/icons-react"

import { Button } from "@/components/ui/button"
import { Kbd } from "@/components/ui/kbd"

export function KbdButton() {
  return (
    <div className="flex flex-wrap items-center justify-center gap-3">
      <Button variant="outline">
        <IconSearch data-icon="inline-start" />
        Search
        <Kbd keys="mod+k" />
      </Button>
      <Button>
        Save
        <Kbd keys="mod+s" />
      </Button>
      <Button variant="ghost">
        Undo
        <Kbd keys="mod+z" variant="flat" />
      </Button>
    </div>
  )
}

Icons and actions

Put icons in a cap for actions without a key name, and give them a visually hidden label.

ShiftClickto select a range

Backspaceto go back

import { IconArrowBackUp, IconClick } from "@tabler/icons-react"

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

export function KbdComposed() {
  return (
    <div className="flex flex-col items-center gap-3 text-sm text-muted-foreground">
      <p className="flex items-center gap-2">
        <KbdGroup>
          <Kbd keys="shift" />
          <Kbd>
            <IconClick aria-hidden="true" />
            <span className="sr-only">Click</span>
          </Kbd>
        </KbdGroup>
        to select a range
      </p>
      <p className="flex items-center gap-2">
        <Kbd>
          <IconArrowBackUp aria-hidden="true" />
          <span className="sr-only">Backspace</span>
        </Kbd>
        to go back
      </p>
    </div>
  )
}

Right to left

Shortcuts stay in left-to-right order inside right-to-left text, the way they're printed on the keyboard.

اضغطShiftCommandPلفتح لوحة الأوامر، أو Escape للإغلاق

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

export function KbdRtl() {
  return (
    <p
      dir="rtl"
      className="flex items-center gap-2 text-sm text-muted-foreground"
    >
      اضغط
      <KbdGroup keys="mod+shift+p" />
      لفتح لوحة الأوامر، أو <Kbd keys="escape" /> للإغلاق
    </p>
  )
}
  • Symbols like ⌘ and ⇧ are hidden from screen readers and replaced with their names, so keys="mod+shift+p" is read as "Command Shift P".
  • Caps render as <kbd>, and a group nests them in another <kbd>, which is how HTML marks a key combination.
  • Showing a shortcut doesn't bind it. Register the key handler yourself.
  • The press effect is decoration, with a color change in place of movement when reduced motion is on. Before hydration, every platform sees the Apple symbols.

Both parts render a <kbd> and accept render and its attributes.

PropTypeDefault
keysKeys joined with +, like "mod+shift+p". mod is ⌘ on Apple devices and Ctrl elsewhere. Names such as alt, enter, escape, up and space become symbols or short words, and get a spoken name for screen readers.
string–
variantInherited from KbdGroup when unset.
"keycap" | "flat""keycap"
sizeInherited from KbdGroup when unset.
"sm" | "default" | "lg""default"
listenPress the cap while its real key is held. Works with keys or a plain key name as children.
booleanfalse
render
ReactElement | (props, state) => ReactElement<kbd>
AttributeDescription
data-slot="kbd"Target caps in CSS.
data-variantThe current variant.
data-sizeThe current size.
data-pressedPresent while the real key is held.
PropTypeDefault
keysKeys joined with +, like "mod+shift+p". mod is ⌘ on Apple devices and Ctrl elsewhere. Names such as alt, enter, escape, up and space become symbols or short words, and get a spoken name for screen readers. Spaces separate the steps of a sequence.
string–
separatorShown between the steps of a sequence.
ReactNode"then"
variantPassed to every cap inside.
"keycap" | "flat"–
sizePassed to every cap inside.
"sm" | "default" | "lg"–
listenPassed to every cap inside.
boolean–
render
ReactElement | (props, state) => ReactElement<kbd>
AttributeDescription
data-slot="kbd-group"Target groups in CSS.
data-slot="kbd-separator"The text between sequence steps.