Kbd
A visual representation of a keyboard key or shortcut.
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
| Part | Element | Description |
|---|---|---|
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.
<Kbd.Root> <Kbd.Key name="mod" /> <Kbd.Key name="shift" /> <Kbd.Key>P</Kbd.Key></Kbd.Root>name | Apple | Other | Announced as |
|---|---|---|---|
mod | ⌘ | Ctrl | Command / Control |
meta | ⌘ | Win | Command / Windows |
ctrl | ⌃ | Ctrl | Control |
alt | ⌥ | Alt | Option / Alt |
shift | ⇧ | Shift | Shift |
enter | ⏎ | Enter | Enter |
escape | Esc | Esc | Escape |
tab | ⇥ | Tab | Tab |
backspace | ⌫ | Backspace | Backspace |
delete | ⌦ | Del | Delete |
space | Space | Space | Space |
up down left right | ↑ ↓ ← → | ↑ ↓ ← → | Up Arrow, … |
home end | ↖ ↘ | Home End | Home, End |
pageup pagedown | ⇞ ⇟ | PgUp PgDn | Page 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.
<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 headerconst 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.
<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.
<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.
<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> | — |