Components Kbd

Petal Pro is the full SaaS app this is built for

Auth, billing, admin, and Claude Code integration included. One purchase, unlimited projects.

Kbd

Keyboard keys that look like keyboard keys - single keys, combos with separators, sized to match your text.
A single key

One <kbd> element with the key cap treatment. Use it inline in a sentence when you are telling someone which key to press.

Press / to jump to search, or Esc to close anything that is open.

heex
<p class="text-sm text-gray-500 dark:text-gray-400">
Press
<.kbd>/</.kbd>
to jump to search, or
<.kbd>Esc</.kbd>
to close anything that is open.
</p>
Key sequences

keys renders one chip per key with the separator glyph between them. Known names fold to their symbol (cmd becomes the command glyph), anything else renders exactly as you typed it. The separator is hidden from screen readers, which already read the keys.

K P G G I
heex
<div class="flex flex-wrap items-center gap-6">
<.kbd keys={["cmd", "K"]} />
<.kbd keys={["ctrl", "shift", "P"]} />
<.kbd keys={["G"]} separator="then" />
<.kbd keys={["G", "I"]} separator="then" />
</div>
Sizes

md is the default and matches the chips already used by the command palette trigger and input group addons. sm is the dense one for table rows, sidebars and menu items.

S S
heex
<div class="flex items-center gap-6">
<.kbd keys={["cmd", "S"]} size="md" />
<.kbd keys={["cmd", "S"]} size="sm" />
</div>
A shortcuts cheat sheet

The row-and-chip layout every app ends up building. Small chips keep the rows tight, and the whole table reads fine with only the keys announced.

Open command palette K
New issue C
Assign to me A I
Save and close
heex
<div class="max-w-md divide-y divide-gray-200 rounded-xl border border-gray-200 dark:divide-gray-800 dark:border-gray-800">
<div class="flex items-center justify-between gap-4 px-4 py-2.5">
<span class="text-sm text-gray-700 dark:text-gray-300">Open command palette</span>
<.kbd keys={["cmd", "K"]} size="sm" />
</div>
<div class="flex items-center justify-between gap-4 px-4 py-2.5">
<span class="text-sm text-gray-700 dark:text-gray-300">New issue</span>
<.kbd keys={["C"]} size="sm" />
</div>
<div class="flex items-center justify-between gap-4 px-4 py-2.5">
<span class="text-sm text-gray-700 dark:text-gray-300">Assign to me</span>
<.kbd keys={["A", "I"]} separator="then" size="sm" />
</div>
<div class="flex items-center justify-between gap-4 px-4 py-2.5">
<span class="text-sm text-gray-700 dark:text-gray-300">Save and close</span>
<.kbd keys={["cmd", "enter"]} size="sm" />
</div>
</div>

The shortcut sits at the end of the row on ml-auto. This is the pattern the dropdown examples already use by hand; the component gives you the symbol map for free.

heex
<div class="max-w-xs rounded-xl border border-gray-200 p-1.5 dark:border-gray-800">
<button
type="button"
class="flex w-full items-center gap-2 rounded-lg px-2 py-1.5 text-sm text-gray-700 hover:bg-gray-100 dark:text-gray-300 dark:hover:bg-gray-800"
>
<.icon name="hero-pencil-square" class="h-4 w-4 text-gray-400" /> Rename
<.kbd keys={["cmd", "E"]} size="sm" class="ml-auto" />
</button>
<button
type="button"
class="flex w-full items-center gap-2 rounded-lg px-2 py-1.5 text-sm text-gray-700 hover:bg-gray-100 dark:text-gray-300 dark:hover:bg-gray-800"
>
<.icon name="hero-document-duplicate" class="h-4 w-4 text-gray-400" /> Duplicate
<.kbd keys={["cmd", "D"]} size="sm" class="ml-auto" />
</button>
<button
type="button"
class="flex w-full items-center gap-2 rounded-lg px-2 py-1.5 text-sm text-danger-600 hover:bg-danger-50 dark:text-danger-400 dark:hover:bg-danger-500/10"
>
<.icon name="hero-trash" class="h-4 w-4" /> Delete
<.kbd keys={["cmd", "backspace"]} size="sm" class="ml-auto" />
</button>
</div>
Properties
Attribute Type Default Description
class any nil CSS class
keys list nil renders a key sequence with separator glyphs, e.g. keys={["cmd", "K"]}. Known names ("cmd", "shift", "alt", "ctrl", "enter", "esc", "tab", "backspace", arrows) map to their symbols; unknown strings render verbatim
rest global any extra HTML attributes, e.g. title or data-*
separator string "+" glyph between keys in a sequence; nil renders the keys with no separator at all
size string "md" chip size. sm is the dense variant for table rows and sidebars
one of: "sm", "md"
:inner_block slot slot single-key content when keys is not used