Back to Sentry

Hotkey

static/app/components/core/hotkey/hotkey.mdx

26.6.06.3 KB
Original Source

import {Hotkey, Kbd} from '@sentry/scraps/hotkey'; import {Flex} from '@sentry/scraps/layout'; import {Text} from '@sentry/scraps/text'; import {InlineCode} from '@sentry/scraps/code'; import {IconCommand, IconControl, IconShift, IconOption, IconReturn} from 'sentry/icons';

import * as Storybook from 'sentry/stories';

export const documentation = import('!!type-loader!@sentry/scraps/hotkey');

The <Hotkey> component displays keyboard shortcuts using semantic <kbd> elements with automatic platform-aware modifier rendering. The mod token resolves to <InlineCode><IconCommand size="xs" /></InlineCode> on macOS and Ctrl on Windows/Linux, so a single shortcut definition works everywhere. The <Kbd> component renders a single styled key cap.

Hotkey

<Hotkey> accepts the same key string format as the useHotkeys hook, making it easy to display the shortcuts you've already registered.

Prefer mod for the platform-primary modifier — it's the OS-agnostic shorthand for "command on Mac, ctrl elsewhere" and renders the correct glyph automatically.

<Storybook.Demo> <Flex gap="xl" align="center"> <Flex gap="sm" align="center"> <Text>Save:</Text> <Hotkey value="mod+s" /> </Flex> <Flex gap="sm" align="center"> <Text>Search:</Text> <Hotkey value="mod+k" /> </Flex> <Flex gap="sm" align="center"> <Text>Copy:</Text> <Hotkey value="mod+c" /> </Flex> </Flex> </Storybook.Demo>

jsx
<Hotkey value="mod+s" />
<Hotkey value="mod+k" />
<Hotkey value="mod+c" />

Platform Mapping

Modifiers are automatically mapped per platform. No fallback arrays needed for standard modifier differences.

Key nameMacOther platforms
mod<InlineCode><IconCommand size="xs" /></InlineCode>Ctrl
command<InlineCode><IconCommand size="xs" /></InlineCode>Ctrl
ctrl<InlineCode><IconControl size="xs" /></InlineCode>Ctrl
alt<InlineCode><IconOption size="xs" /></InlineCode>Alt
option<InlineCode><IconOption size="xs" /></InlineCode>Alt
shift<InlineCode><IconShift size="xs" /></InlineCode><InlineCode><IconShift size="xs" /></InlineCode>
enter<InlineCode><IconReturn size="xs" /></InlineCode><InlineCode><IconReturn size="xs" /></InlineCode>
return<InlineCode><IconReturn size="xs" /></InlineCode><InlineCode><IconReturn size="xs" /></InlineCode>

Use command or ctrl directly only as an escape hatch for shortcuts that intentionally bind to a specific modifier on every platform (e.g. a Mac-only dev tool, or a shortcut that must be Ctrl on macOS).

Keys without an explicit glyph mapping (e.g. Home, End, PageUp) render as title-cased text.

Complex Shortcuts

<Storybook.Demo> <Flex gap="xl" align="center"> <Flex gap="sm" align="center"> <Text>Undo:</Text> <Hotkey value="mod+z" /> </Flex> <Flex gap="sm" align="center"> <Text>Redo:</Text> <Hotkey value="mod+shift+z" /> </Flex> <Flex gap="sm" align="center"> <Text>Navigate:</Text> <Hotkey value="shift+right" /> </Flex> </Flex> </Storybook.Demo>

jsx
<Hotkey value="mod+z" />
<Hotkey value="mod+shift+z" />
<Hotkey value="shift+right" />

Array Form

Pass an array when the actual keys (not just modifiers) differ across platforms. The first combo is used. With mod, you rarely need this — only reach for it when the non-modifier keys themselves differ.

jsx
<Hotkey value={['mod+backspace', 'delete']} />

Variants

Both <Hotkey> and <Kbd> accept a variant prop. The default embossed variant renders a raised key cap; debossed renders a sunken, muted key cap suited for use on filled or dark backgrounds.

<Storybook.Demo> <Flex gap="xl" align="center"> <Flex gap="sm" align="center"> <Text>Embossed (default):</Text> <Hotkey value="mod+k" variant="embossed" /> </Flex> <Flex gap="sm" align="center"> <Text>Debossed:</Text> <Hotkey value="mod+k" variant="debossed" /> </Flex> </Flex> </Storybook.Demo>

jsx
<Hotkey value="mod+k" />
<Hotkey value="mod+k" variant="debossed" />

<Kbd>Esc</Kbd>
<Kbd variant="debossed">Esc</Kbd>

Kbd

<Kbd> renders a single styled key cap. Use it for standalone key references in prose or documentation.

<Storybook.Demo> <Flex gap="lg" align="center"> <Text> Press <Kbd>Esc</Kbd> to close </Text> <Text> Use <Kbd>Tab</Kbd> to navigate </Text> <Text> Hit <Kbd>Enter</Kbd> to confirm </Text> </Flex> </Storybook.Demo>

jsx
<Text>
  Press <Kbd>Esc</Kbd> to close
</Text>
<Text>
  Use <Kbd>Tab</Kbd> to navigate
</Text>

useHotkeys Hook

The useHotkeys hook registers keyboard shortcuts. It uses the same key string format as <Hotkey>, so you can display exactly what you register.

jsx
import {Hotkey, useHotkeys} from '@sentry/scraps/hotkey';

function MyComponent() {
  useHotkeys([
    {match: 'mod+k', callback: () => openSearch()},
    {match: 'mod+/', callback: () => openHelp()},
  ]);

  return (
    <Text>
      Search: <Hotkey value="mod+k" />
    </Text>
  );
}

Matching is layout-aware: an AZERTY user pressing the key labeled K fires command+k, and shortcuts like command+shift+1 continue to work regardless of how Shift+1 produces a character on the user's layout.

Conditional hotkeys

Use enabled to gate a hotkey on state without rebuilding the array. While enabled is false the callback never fires and preventDefault is never called, so the key reaches other listeners unchanged.

jsx
useHotkeys([
  {
    match: 'Escape',
    enabled: isPanelOpen,
    callback: closePanel,
  },
]);

Accessibility

Both <Hotkey> and <Kbd> render semantic <kbd> HTML elements. <Hotkey> uses nested <kbd> elements (an outer <kbd> wrapping inner <kbd> elements for each key), which is the correct HTML semantics for a key combination.