Skip to content

Kbd

A visual representation of a keyboard key or shortcut.

Control K
import { Kbd } from '@ontoui/react';
<Kbd.Root>
<Kbd.Key name="mod" />
<Kbd.Key>K</Kbd.Key>
</Kbd.Root>;

name="mod" is the portable primary modifier — it draws on Apple platforms and Ctrl everywhere else, and announces itself in words to screen readers. Keys with nothing to translate, like K, are passed as children.

Anatomy

PartElementDescription
Kbd.Root<kbd>The shortcut container. Lays the keys out in a row.
Kbd.Key<kbd>A single key cap.
Kbd.Separator<span>A decorative joiner between keys. Hidden from screen readers.

Nesting Kbd.Key inside Kbd.Root is the HTML-native way to mark up a key combination — the outer <kbd> is the shortcut, each inner <kbd> is one key of it.

Kbd is presentational — it displays a shortcut but does not bind one. Register the actual key handler yourself and keep the two in sync.

Examples

Named keys

Pass name for any key whose legend depends on the platform, or whose symbol reads badly out loud. Each name resolves to the right glyph and carries its spoken label with it, so ⌘⇧P announces as “Command Shift P” rather than as a run of unnamed symbols.

Control Shift PAlt Enter Escape Up Arrow Down Arrow
<Kbd.Root>
<Kbd.Key name="mod" />
<Kbd.Key name="shift" />
<Kbd.Key>P</Kbd.Key>
</Kbd.Root>
nameAppleOtherAnnounced as
modCtrlCommand / Control
metaWinCommand / Windows
ctrlCtrlControl
altAltOption / Alt
shiftShiftShift
enterEnterEnter
escapeEscEscEscape
tabTabTab
backspaceBackspaceBackspace
deleteDelDelete
spaceSpaceSpaceSpace
up down left right Up Arrow, …
home end Home EndHome, End
pageup pagedown PgUp PgDnPage Up, Page Down

Pass children alongside name to keep the spoken label but draw something else — useful when you prefer a spelled-out modifier: <Kbd.Key name="mod">Cmd</Kbd.Key>.

Platform

Kbd detects Apple platforms on the client and picks the matching legends. Set platform on Kbd.Root to override that — for a keyboard-shortcut cheatsheet that documents both, or to render correctly on the server when you already know the platform from a User-Agent hint.

platform="apple"Command Option Backspace
platform="other"Control Alt Backspace
<Kbd.Root platform="apple">
<Kbd.Key name="mod" />
<Kbd.Key name="alt" />
<Kbd.Key name="backspace" />
</Kbd.Root>
<Kbd.Root platform="other">
<Kbd.Key name="mod" />
<Kbd.Key name="alt" />
<Kbd.Key name="backspace" />
</Kbd.Root>

Individual keys accept platform too, overriding the root.

Server rendering

The platform cannot be detected during a server render, so platform="auto" emits the portable legends (Ctrl, Alt) into the HTML and switches to the Apple glyphs once hydrated. This is hydration-safe — no mismatch warning — but Apple users see one frame of Ctrl before it settles. To avoid that flash, resolve the platform yourself and pass it explicitly:

// Next.js — from a Client Hints header
const platform = headers().get('sec-ch-ua-platform') === '"macOS"' ? 'apple' : 'other';
<Kbd.Root platform={platform}>
<Kbd.Key name="mod" />
<Kbd.Key>K</Kbd.Key>
</Kbd.Root>;

In a client-only app there is no flash — the first render already reads the real platform.

Separators

Keys sit next to each other with no separator by default. Add Kbd.Separator between them when the combination needs a joiner — + for keys pressed together, or a word like then for keys pressed in sequence. Separators are decorative and hidden from screen readers, so they never interrupt the spoken shortcut.

Control Shift PGP
<Kbd.Root>
<Kbd.Key name="mod" />
<Kbd.Separator>+</Kbd.Separator>
<Kbd.Key name="shift" />
<Kbd.Separator>+</Kbd.Separator>
<Kbd.Key>P</Kbd.Key>
</Kbd.Root>
<Kbd.Root>
<Kbd.Key>G</Kbd.Key>
<Kbd.Separator>then</Kbd.Separator>
<Kbd.Key>P</Kbd.Key>
</Kbd.Root>

Inline in text

Kbd.Root is inline and inherits the surrounding font, so a shortcut drops into a sentence without disturbing the line.

Press Escape to close the dialog, or Enter to confirm.
<p>
Press{' '}
<Kbd.Root>
<Kbd.Key name="escape" />
</Kbd.Root>{' '}
to close the dialog.
</p>

Naming an unlisted key

Named keys label themselves, and plain letters read correctly on their own. A symbol that is neither — `, /, [ — has no spoken form, and <kbd> carries no ARIA role, so aria-label on it alone is not reliably exposed. Give the whole shortcut role="img" and aria-label instead; the pair makes it a single labelled object and supersedes the names of the keys inside it.

Control `
<Kbd.Root role="img" aria-label="Control Backtick">
<Kbd.Key name="ctrl" />
<Kbd.Key>`</Kbd.Key>
</Kbd.Root>

API Reference

Kbd.Root

Prop Type Default
children? ReactNode
id? string
className? string
platform? "auto" | "apple" | "other" 'auto'
aria-label? string
role? string
ref? Ref<HTMLElement>

Kbd.Key

Prop Type Default
children? ReactNode
className? string
name? "mod" | "meta" | "ctrl" | "alt" | "shift" | "enter" | "escape" | "tab" | "backspace" | "delete" | "space" | "up" | "down" | "left" | "right" | "home" | "end" | "pageup" | "pagedown"
platform? "auto" | "apple" | "other" 'auto'
ref? Ref<HTMLElement>

Kbd.Separator

Prop Type Default
children? ReactNode
className? string
ref? Ref<HTMLElement>