Skip to content
Drivn
7 min read

React Kbd Component Examples

Drop-in React Kbd examples: single keys, Mac ⌘K shortcuts, outline variant, inline hints, palette triggers, and tooltip cues. Zero runtime deps, TypeScript.

Every product UI ships shortcut hints — the ⌘K glyph next to a search bar, the Esc next to a dialog close, the Ctrl + Shift + P inside a command palette tooltip. The Kbd component in Drivn is a small wrapper around the native HTML <kbd> element that styles a key with font-mono text-xs font-medium, a fixed h-5 height, and a muted background. The file is about thirty lines of TypeScript, ships zero runtime UI dependencies, and exposes two variants (default and outline) plus a Kbd.Group static property for rendering chord sequences like ⌘ + K as a single visual unit.

This page collects the patterns that come up in shipped UIs. Each example is copy-paste ready and uses the same @/components/ui/kbd import path the Drivn CLI installs under, so the snippets compile the moment you run npx drivn add kbd. The component's styles.base constant includes a CSS rule — [&_svg:not([class*=size-])]:size-3 — that auto-sizes any unsized SVG descendant to twelve pixels, which means Lucide icons like <Command /> and <Option /> slot in as Kbd children without any extra className.

The examples below cover the basic single-key render, the default versus outline variants, a Mac-style ⌘K chord using Kbd.Group, a Windows-style Ctrl + Shift + P chord, an inline shortcut hint inside body text, a Command palette trigger that opens on the key combo, and a Tooltip with a Kbd hint inside. Every snippet is TypeScript — Drivn ships TypeScript-only.

Basic single key

The minimum viable Kbd is one element with the key label as children. Drop the component into JSX, pass a string like "Shift" or "Esc", and the rendered output is a small pill at h-5 (twenty pixels tall) with a bg-muted background, text-muted-foreground foreground, font-mono family, and text-xs size. The pointer-events-none and select-none rules in styles.base make the key non-interactive and unselectable, which matches how a physical keyboard key behaves visually inside body text.

The default variant uses the filled bg-muted text-muted-foreground recipe. There is no onClick handler on the Kbd itself — keyboard shortcuts trigger from a parent listener (a useEffect with window.addEventListener("keydown", ...)), and the Kbd is purely the visual hint that tells the user what to press. Pair single Kbd elements inline with body copy when you describe a one-key shortcut like Esc to close a Dialog.

1import { Kbd } from "@/components/ui/kbd"
2
3export default function Page() {
4 return (
5 <Kbd>Shift</Kbd>
6 )
7}

Default and outline variants

The variant prop accepts default or outline. The default variant uses bg-muted text-muted-foreground — a filled pill that reads as a solid key against a light or dark page background. The outline variant uses border border-border text-muted-foreground — a hairline-bordered pill with no fill, which works better when the Kbd sits inside an already-tinted surface like a Tooltip, a Toast, or a Card header where a second muted fill would visually compete.

The prop is typed as keyof typeof styles.variants, so the editor autocompletes the two valid strings and a typo (variant="solid") is a compile error. Add a third variant after install by extending the styles.variants object in your local @/components/ui/kbd file — the prop type updates automatically. The full surface ships in roughly thirty lines, so the customization edit lives in one screen of code.

1import { Kbd } from "@/components/ui/kbd"
2
3export default function Page() {
4 return (
5 <div className="flex items-center gap-2">
6 <Kbd variant="default">Shift</Kbd>
7 <Kbd variant="outline">Shift</Kbd>
8 </div>
9 )
10}

Mac shortcut: ⌘ + K with Kbd.Group

A two-key chord like ⌘ + K renders as two <Kbd> elements nested inside a <Kbd.Group> wrapper. The Group primitive is exposed via Object.assign(KbdRoot, { Group }) so the import is a single name — Kbd — and the call site reads as one parent with two children. The Group element applies inline-flex items-center gap-1 so the keys sit on one line with a four-pixel gap between them, matching the visual rhythm of a real keyboard.

The Command glyph comes from lucide-react as the Command icon. Drop it inside the first Kbd as a child — Drivn's styles.base includes the rule [&_svg:not([class*=size-])]:size-3 so the icon auto-sizes to twelve pixels without any className on the icon. The second Kbd holds the letter "K". The result is the standard ⌘K chord you see next to a search input at the top of every modern app. Pair this with the Command component to open a palette on the key combo.

1import { Kbd } from "@/components/ui/kbd"
2import { Command } from "lucide-react"
3
4export default function Page() {
5 return (
6 <Kbd.Group>
7 <Kbd><Command /></Kbd>
8 <Kbd>K</Kbd>
9 </Kbd.Group>
10 )
11}

Windows shortcut: Ctrl + Shift + P

For three-key chords like the VS Code command palette trigger Ctrl + Shift + P, nest three Kbd elements inside one Kbd.Group. The gap-1 between siblings handles the spacing — there are no explicit + separators because the inline-flex layout reads as a sequence visually. If you want a literal + glyph between keys for typographic clarity, drop a <span className="text-muted-foreground">+</span> between the Kbd elements, but most product UIs skip it because the spacing already communicates the chord.

The Group renders all three keys as default variant pills. To match a documentation aesthetic that prefers thin outlines, pass variant="outline" on each Kbd inside the Group — the variants compose freely because the prop sits on each Kbd individually, not on the Group wrapper. The full chord renders in seven lines of JSX with one import.

1import { Kbd } from "@/components/ui/kbd"
2
3export default function Page() {
4 return (
5 <Kbd.Group>
6 <Kbd>Ctrl</Kbd>
7 <Kbd>Shift</Kbd>
8 <Kbd>P</Kbd>
9 </Kbd.Group>
10 )
11}

Inline shortcut hint in body text

A common pattern is a sentence that ends with a chord — "Press ⌘K to open the search dialog." The Kbd Group nests inside a <p> tag with surrounding text on either side, separated by {' '} JSX whitespace tokens so the spaces survive Prettier. The Kbd sits at h-5 and align-items: center from the inline-flex on the Group, which puts the key centered on the text baseline of the paragraph.

The inline pattern is the most common shortcut hint in product UIs — settings pages, onboarding tooltips, empty states. Pair it with the Tooltip component for a hover hint, or with the Toast component for a transient confirmation that includes a shortcut to undo. The Kbd reads as part of the sentence because the font-mono family contrasts cleanly with the font-sans body text without breaking the line height.

1import { Kbd } from "@/components/ui/kbd"
2import { Command } from "lucide-react"
3
4export default function Page() {
5 return (
6 <p className="text-sm text-muted-foreground">
7 Press{" "}
8 <Kbd.Group>
9 <Kbd><Command /></Kbd>
10 <Kbd>K</Kbd>
11 </Kbd.Group>
12 {" "}to open the search dialog.
13 </p>
14 )
15}

Command palette trigger with keyboard listener

A real ⌘K shortcut needs a useEffect that listens for the key combination and opens the palette. The pattern is a keydown listener on window that checks event.metaKey && event.key === "k", calls event.preventDefault() to stop the browser default (locating the search bar), and toggles a useState boolean that controls the Command palette's open state. Pair the Kbd visually next to the search input that opens the palette so the user sees what to press.

The listener runs only on the client, so the component file needs "use client" at the top. Clean up the listener in the useEffect return so it does not stack across re-renders. The same pattern handles Ctrl + K on Windows and Linux by checking event.ctrlKey instead of event.metaKey, or both with (event.metaKey || event.ctrlKey) if you want one shortcut that works across platforms. See the Command component reference for the full palette implementation.

1"use client"
2import { Kbd } from "@/components/ui/kbd"
3import { Command } from "lucide-react"
4import { useEffect, useState } from "react"
5
6export default function SearchTrigger() {
7 const [open, setOpen] = useState(false)
8
9 useEffect(() => {
10 function onKeyDown(event: KeyboardEvent) {
11 if ((event.metaKey || event.ctrlKey) && event.key === "k") {
12 event.preventDefault()
13 setOpen((prev) => !prev)
14 }
15 }
16 window.addEventListener("keydown", onKeyDown)
17 return () => window.removeEventListener("keydown", onKeyDown)
18 }, [])
19
20 return (
21 <button
22 onClick={() => setOpen(true)}
23 className="inline-flex items-center gap-2 rounded-md border px-3 py-1.5 text-sm"
24 >
25 <span className="text-muted-foreground">Search...</span>
26 <Kbd.Group>
27 <Kbd variant="outline"><Command /></Kbd>
28 <Kbd variant="outline">K</Kbd>
29 </Kbd.Group>
30 </button>
31 )
32}
Get started

Install Drivn in one command

Copy the source into your project and own every line. Zero runtime dependencies, pure React + Tailwind.

npx drivn@latest create

Requires Node 18+. Works with npm, pnpm, and yarn.

Enjoying Drivn?
Star the repo on GitHub to follow new component releases.
Star →

Frequently asked questions

Import the Command icon from lucide-react and drop it directly inside the Kbd as the child — no className, no wrapper. Drivn's Kbd styles.base includes the rule [&_svg:not([class*=size-])]:size-3 so any unsized SVG descendant auto-sizes to twelve pixels. The icon fits the twenty-pixel-tall Kbd pill without manual sizing. To override the size, pass an explicit className="size-4" on the icon and the not() matcher in the rule excludes it, letting your explicit class win.

Use variant="outline" when the Kbd sits inside a surface that already carries a muted fill — a Tooltip bubble, a Toast container, a Card header, a search trigger with a tinted background. The default bg-muted would visually compete with the surrounding fill and read as a flat patch. The outline variant uses border border-border for a hairline edge and inherits the surrounding background, which keeps the key readable without adding a second fill layer.

Yes — the Group is a layout primitive with inline-flex items-center gap-1 and no maximum-children rule. Chord sequences with four or more keys like Ctrl + Alt + Shift + S render the same way: drop the Kbd elements one after another inside the Group. The gap between siblings handles spacing automatically. For documentation that shows a sequence of separate shortcuts (press X, then Y), use two separate Groups separated by prose rather than one long Group, because the gap inside a single Group reads as a simultaneous chord rather than a sequence.

No — the Kbd is purely the visual hint. The component renders a styled <kbd> element and has no event listeners attached. To wire a real shortcut, add a useEffect in the parent component that calls window.addEventListener("keydown", handler) and checks event.metaKey, event.ctrlKey, and event.key against the chord you want to trigger. Pair the listener with setOpen state for a Command palette or any other shortcut behavior. The Kbd just tells the user what to press.

Yes. The Kbd file has no "use client" directive because it uses no client-only APIs — no useState, no useEffect, no event handlers in the component body. You can render Kbd elements inside a Server Component as static shortcut hints in documentation, marketing pages, or layout headers. Only the surrounding palette logic (the keydown listener) needs the client boundary, and that lives in its own Client Component. Next.js draws the boundary based on the consumer file, so Kbd works in both rendering modes without configuration.