Docs
CodeRabbit
Cloudflare
AG Grid
SerpAPI
Netlify
OpenRouter
Neon
WorkOS
Clerk
Electric
PowerSync
Sentry
Railway
Prisma
Strapi
Unkey
CodeRabbit
Cloudflare
AG Grid
SerpAPI
Netlify
OpenRouter
Neon
WorkOS
Clerk
Electric
PowerSync
Sentry
Railway
Prisma
Strapi
Unkey
Hotkeys API Reference
Hotkey Sequence API Reference
Key hold & held keys API Reference
Hotkey Recorder API Reference
Hotkey Sequence Recorder API Reference
Normalization & format API Reference

Solid Example: CreateHotkeys

import { For, Show, createSignal } from 'solid-js'
import { render } from 'solid-js/web'
import {
  HotkeysProvider,
  createHotkeyRegistrations,
  createHotkeys,
  formatForDisplay,
} from '@tanstack/solid-hotkeys'
import { hotkeysDevtoolsPlugin } from '@tanstack/solid-hotkeys-devtools'
import { TanStackDevtools } from '@tanstack/solid-devtools'
import type { CreateHotkeyDefinition, Hotkey } from '@tanstack/solid-hotkeys'
import './index.css'

const plugins = [hotkeysDevtoolsPlugin()]

function BasicMultiHotkeys() {
  const [log, setLog] = createSignal<Array<string>>([])
  const [saveCount, setSaveCount] = createSignal(0)
  const [undoCount, setUndoCount] = createSignal(0)
  const [redoCount, setRedoCount] = createSignal(0)

  createHotkeys([
    {
      hotkey: 'Shift+S',
      callback: (_e, { hotkey }) => {
        setSaveCount((c) => c + 1)
        setLog((l) => [`${hotkey} pressed`, ...l].slice(0, 20))
      },
      options: {
        meta: { name: 'Save', description: 'Save the current document' },
      },
    },
    {
      hotkey: 'Shift+U',
      callback: (_e, { hotkey }) => {
        setUndoCount((c) => c + 1)
        setLog((l) => [`${hotkey} pressed`, ...l].slice(0, 20))
      },
      options: {
        meta: { name: 'Undo', description: 'Undo the last action' },
      },
    },
    {
      hotkey: 'Shift+R',
      callback: (_e, { hotkey }) => {
        setRedoCount((c) => c + 1)
        setLog((l) => [`${hotkey} pressed`, ...l].slice(0, 20))
      },
      options: {
        meta: { name: 'Redo', description: 'Redo the last undone action' },
      },
    },
  ])

  return (
    <div class="demo-section">
      <h2>Basic Multi-Hotkey Registration</h2>
      <p>
        All three hotkeys are registered in a single{' '}
        <code>createHotkeys()</code> call with <code>meta</code> for name and
        description.
      </p>
      <div class="hotkey-grid">
        <div>
          <kbd>{formatForDisplay('Shift+S' as Hotkey)}</kbd> Save ({saveCount()}
          )
        </div>
        <div>
          <kbd>{formatForDisplay('Shift+U' as Hotkey)}</kbd> Undo ({undoCount()}
          )
        </div>
        <div>
          <kbd>{formatForDisplay('Shift+R' as Hotkey)}</kbd> Redo ({redoCount()}
          )
        </div>
      </div>
      <Show when={log().length > 0}>
        <div class="log">
          <For each={log()}>
            {(entry) => <div class="log-entry">{entry}</div>}
          </For>
        </div>
      </Show>
      <pre class="code-block">{`createHotkeys([
  {
    hotkey: 'Shift+S',
    callback: () => save(),
    options: { meta: { name: 'Save', description: 'Save the document' } },
  },
  {
    hotkey: 'Shift+U',
    callback: () => undo(),
    options: { meta: { name: 'Undo', description: 'Undo the last action' } },
  },
])`}</pre>
    </div>
  )
}

function CommonOptionsDemo() {
  const [enabled, setEnabled] = createSignal(true)
  const [counts, setCounts] = createSignal({ a: 0, b: 0, c: 0 })

  createHotkeys(
    [
      {
        hotkey: 'Alt+J',
        callback: () => setCounts((c) => ({ ...c, a: c.a + 1 })),
        options: {
          meta: {
            name: 'Action A',
            description: 'First action (respects toggle)',
          },
        },
      },
      {
        hotkey: 'Alt+K',
        callback: () => setCounts((c) => ({ ...c, b: c.b + 1 })),
        options: {
          meta: {
            name: 'Action B',
            description: 'Second action (respects toggle)',
          },
        },
      },
      {
        hotkey: 'Alt+L',
        callback: () => setCounts((c) => ({ ...c, c: c.c + 1 })),
        options: {
          enabled: true,
          meta: {
            name: 'Action C',
            description: 'Always-on action (overrides toggle)',
          },
        },
      },
    ],
    () => ({ enabled: enabled() }),
  )

  return (
    <div class="demo-section">
      <h2>Common Options with Per-Hotkey Overrides</h2>
      <p>
        <kbd>{formatForDisplay('Alt+J' as Hotkey)}</kbd> and{' '}
        <kbd>{formatForDisplay('Alt+K' as Hotkey)}</kbd> respect the global
        toggle. <kbd>{formatForDisplay('Alt+L' as Hotkey)}</kbd> overrides{' '}
        <code>enabled: true</code> so it always works.
      </p>
      <div style={{ 'margin-bottom': '12px' }}>
        <button onClick={() => setEnabled((e) => !e)}>
          {enabled() ? 'Disable' : 'Enable'} common hotkeys
        </button>
      </div>
      <div class="hotkey-grid">
        <div>
          <kbd>{formatForDisplay('Alt+J' as Hotkey)}</kbd> Action A (
          {counts().a})
        </div>
        <div>
          <kbd>{formatForDisplay('Alt+K' as Hotkey)}</kbd> Action B (
          {counts().b})
        </div>
        <div>
          <kbd>{formatForDisplay('Alt+L' as Hotkey)}</kbd> Action C (
          {counts().c})<span class="hint"> (always on)</span>
        </div>
      </div>
      <pre class="code-block">{`createHotkeys(
  [
    { hotkey: 'Alt+J', callback: () => actionA(),
      options: { meta: { name: 'Action A' } } },
    { hotkey: 'Alt+L', callback: () => actionC(),
      options: { enabled: true, meta: { name: 'Action C' } } },
  ],
  () => ({ enabled: enabled() }), // common option
)`}</pre>
    </div>
  )
}

interface DynamicShortcut {
  id: number
  hotkey: string
  label: string
  description: string
  count: number
}

let nextId = 0

const DEFAULT_SHORTCUTS: Array<DynamicShortcut> = [
  {
    id: nextId++,
    hotkey: 'Shift+A',
    label: 'Action A',
    description: 'First dynamic action',
    count: 0,
  },
  {
    id: nextId++,
    hotkey: 'Shift+B',
    label: 'Action B',
    description: 'Second dynamic action',
    count: 0,
  },
  {
    id: nextId++,
    hotkey: 'Shift+C',
    label: 'Action C',
    description: 'Third dynamic action',
    count: 0,
  },
]

function DynamicHotkeysDemo() {
  const [shortcuts, setShortcuts] =
    createSignal<Array<DynamicShortcut>>(DEFAULT_SHORTCUTS)
  const [newHotkey, setNewHotkey] = createSignal('')
  const [newLabel, setNewLabel] = createSignal('')
  const [newDescription, setNewDescription] = createSignal('')

  createHotkeys(() =>
    shortcuts().map(
      (s): CreateHotkeyDefinition => ({
        hotkey: s.hotkey as Hotkey,
        callback: () => {
          setShortcuts((prev) =>
            prev.map((item) =>
              item.id === s.id ? { ...item, count: item.count + 1 } : item,
            ),
          )
        },
        options: {
          meta: { name: s.label, description: s.description },
        },
      }),
    ),
  )

  const addShortcut = () => {
    const trimmed = newHotkey().trim()
    if (!trimmed || !newLabel().trim()) return
    setShortcuts((prev) => [
      ...prev,
      {
        id: nextId++,
        hotkey: trimmed,
        label: newLabel().trim(),
        description: newDescription().trim(),
        count: 0,
      },
    ])
    setNewHotkey('')
    setNewLabel('')
    setNewDescription('')
  }

  const removeShortcut = (id: number) => {
    setShortcuts((prev) => prev.filter((s) => s.id !== id))
  }

  return (
    <div class="demo-section">
      <h2>Dynamic Hotkey List</h2>
      <p>
        Add or remove hotkeys at runtime. Because <code>createHotkeys</code>{' '}
        accepts a dynamic array, this works with Solid's reactivity.
      </p>
      <div class="dynamic-list">
        <For each={shortcuts()}>
          {(s) => (
            <div class="dynamic-item">
              <kbd>{formatForDisplay(s.hotkey as Hotkey)}</kbd>
              <span>{s.label}</span>
              <span class="count">{s.count}</span>
              <button onClick={() => removeShortcut(s.id)}>Remove</button>
            </div>
          )}
        </For>
        <Show when={shortcuts().length === 0}>
          <p class="hint">No shortcuts registered. Add one below.</p>
        </Show>
      </div>
      <div class="add-form">
        <input
          type="text"
          placeholder="Hotkey (e.g. Shift+D)"
          value={newHotkey()}
          onInput={(e) => setNewHotkey(e.currentTarget.value)}
          onKeyDown={(e) => {
            if (e.key === 'Enter') addShortcut()
          }}
        />
        <input
          type="text"
          placeholder="Name (e.g. Action D)"
          value={newLabel()}
          onInput={(e) => setNewLabel(e.currentTarget.value)}
          onKeyDown={(e) => {
            if (e.key === 'Enter') addShortcut()
          }}
        />
        <input
          type="text"
          placeholder="Description (optional)"
          value={newDescription()}
          onInput={(e) => setNewDescription(e.currentTarget.value)}
          onKeyDown={(e) => {
            if (e.key === 'Enter') addShortcut()
          }}
        />
        <button onClick={addShortcut} disabled={!newHotkey() || !newLabel()}>
          Add
        </button>
      </div>
      <pre class="code-block">{`const [shortcuts] = createSignal([...])

createHotkeys(
  () => shortcuts().map((s) => ({
    hotkey: s.hotkey,
    callback: s.action,
    options: { meta: { name: s.name, description: s.description } },
  })),
)`}</pre>
    </div>
  )
}

// ---------------------------------------------------------------------------
// Live registrations viewer using createHotkeyRegistrations
// ---------------------------------------------------------------------------

function RegistrationsViewer() {
  const { hotkeys, sequences } = createHotkeyRegistrations()

  return (
    <div class="demo-section">
      <h2>Live Registrations (createHotkeyRegistrations)</h2>
      <p>
        This table is rendered from <code>createHotkeyRegistrations()</code> — a
        reactive view of all registered hotkeys. It updates automatically as
        hotkeys are added, removed, enabled/disabled, or triggered.
      </p>
      <table class="registrations-table">
        <thead>
          <tr>
            <th>Hotkey</th>
            <th>Name</th>
            <th>Description</th>
            <th>Enabled</th>
            <th>Triggers</th>
          </tr>
        </thead>
        <tbody>
          <For each={hotkeys()}>
            {(reg) => (
              <tr>
                <td>
                  <kbd>{formatForDisplay(reg.hotkey)}</kbd>
                </td>
                <td>{reg.options.meta?.name ?? '\u2014'}</td>
                <td class="description-cell">
                  {reg.options.meta?.description ?? '\u2014'}
                </td>
                <td>
                  <span
                    class={
                      reg.options.enabled !== false ? 'status-on' : 'status-off'
                    }
                  >
                    {reg.options.enabled !== false ? 'yes' : 'no'}
                  </span>
                </td>
                <td class="trigger-count">{reg.triggerCount}</td>
              </tr>
            )}
          </For>
          <Show when={hotkeys().length === 0}>
            <tr>
              <td colSpan={5} class="hint">
                No hotkeys registered
              </td>
            </tr>
          </Show>
        </tbody>
      </table>
      <Show when={sequences().length > 0}>
        <h3 style={{ 'margin-top': '16px' }}>Sequences</h3>
        <table class="registrations-table">
          <thead>
            <tr>
              <th>Sequence</th>
              <th>Name</th>
              <th>Description</th>
              <th>Triggers</th>
            </tr>
          </thead>
          <tbody>
            <For each={sequences()}>
              {(reg) => (
                <tr>
                  <td>
                    <For each={reg.sequence}>
                      {(s, i) => (
                        <>
                          {i() > 0 && ' '}
                          <kbd>{formatForDisplay(s)}</kbd>
                        </>
                      )}
                    </For>
                  </td>
                  <td>{reg.options.meta?.name ?? '\u2014'}</td>
                  <td class="description-cell">
                    {reg.options.meta?.description ?? '\u2014'}
                  </td>
                  <td class="trigger-count">{reg.triggerCount}</td>
                </tr>
              )}
            </For>
          </tbody>
        </table>
      </Show>
      <pre class="code-block">{`const { hotkeys, sequences } = createHotkeyRegistrations()

// Render a live table of all registrations
<For each={hotkeys()}>
  {(reg) => (
    <tr>
      <td>{formatForDisplay(reg.hotkey)}</td>
      <td>{reg.options.meta?.name}</td>
      <td>{reg.options.meta?.description}</td>
      <td>{reg.triggerCount}</td>
    </tr>
  )}
</For>`}</pre>
    </div>
  )
}

function App() {
  return (
    <>
      <HotkeysProvider>
        <div class="app">
          <header>
            <h1>createHotkeys</h1>
            <p>
              Register multiple hotkeys in a single primitive call. Supports
              dynamic arrays for variable-length shortcut lists.
            </p>
          </header>
          <BasicMultiHotkeys />
          <CommonOptionsDemo />
          <DynamicHotkeysDemo />
          <RegistrationsViewer />
        </div>
      </HotkeysProvider>
      <TanStackDevtools plugins={plugins} />
    </>
  )
}

const rootElement = document.getElementById('root')!
render(() => <App />, rootElement)
import { For, Show, createSignal } from 'solid-js'
import { render } from 'solid-js/web'
import {
  HotkeysProvider,
  createHotkeyRegistrations,
  createHotkeys,
  formatForDisplay,
} from '@tanstack/solid-hotkeys'
import { hotkeysDevtoolsPlugin } from '@tanstack/solid-hotkeys-devtools'
import { TanStackDevtools } from '@tanstack/solid-devtools'
import type { CreateHotkeyDefinition, Hotkey } from '@tanstack/solid-hotkeys'
import './index.css'

const plugins = [hotkeysDevtoolsPlugin()]

function BasicMultiHotkeys() {
  const [log, setLog] = createSignal<Array<string>>([])
  const [saveCount, setSaveCount] = createSignal(0)
  const [undoCount, setUndoCount] = createSignal(0)
  const [redoCount, setRedoCount] = createSignal(0)

  createHotkeys([
    {
      hotkey: 'Shift+S',
      callback: (_e, { hotkey }) => {
        setSaveCount((c) => c + 1)
        setLog((l) => [`${hotkey} pressed`, ...l].slice(0, 20))
      },
      options: {
        meta: { name: 'Save', description: 'Save the current document' },
      },
    },
    {
      hotkey: 'Shift+U',
      callback: (_e, { hotkey }) => {
        setUndoCount((c) => c + 1)
        setLog((l) => [`${hotkey} pressed`, ...l].slice(0, 20))
      },
      options: {
        meta: { name: 'Undo', description: 'Undo the last action' },
      },
    },
    {
      hotkey: 'Shift+R',
      callback: (_e, { hotkey }) => {
        setRedoCount((c) => c + 1)
        setLog((l) => [`${hotkey} pressed`, ...l].slice(0, 20))
      },
      options: {
        meta: { name: 'Redo', description: 'Redo the last undone action' },
      },
    },
  ])

  return (
    <div class="demo-section">
      <h2>Basic Multi-Hotkey Registration</h2>
      <p>
        All three hotkeys are registered in a single{' '}
        <code>createHotkeys()</code> call with <code>meta</code> for name and
        description.
      </p>
      <div class="hotkey-grid">
        <div>
          <kbd>{formatForDisplay('Shift+S' as Hotkey)}</kbd> Save ({saveCount()}
          )
        </div>
        <div>
          <kbd>{formatForDisplay('Shift+U' as Hotkey)}</kbd> Undo ({undoCount()}
          )
        </div>
        <div>
          <kbd>{formatForDisplay('Shift+R' as Hotkey)}</kbd> Redo ({redoCount()}
          )
        </div>
      </div>
      <Show when={log().length > 0}>
        <div class="log">
          <For each={log()}>
            {(entry) => <div class="log-entry">{entry}</div>}
          </For>
        </div>
      </Show>
      <pre class="code-block">{`createHotkeys([
  {
    hotkey: 'Shift+S',
    callback: () => save(),
    options: { meta: { name: 'Save', description: 'Save the document' } },
  },
  {
    hotkey: 'Shift+U',
    callback: () => undo(),
    options: { meta: { name: 'Undo', description: 'Undo the last action' } },
  },
])`}</pre>
    </div>
  )
}

function CommonOptionsDemo() {
  const [enabled, setEnabled] = createSignal(true)
  const [counts, setCounts] = createSignal({ a: 0, b: 0, c: 0 })

  createHotkeys(
    [
      {
        hotkey: 'Alt+J',
        callback: () => setCounts((c) => ({ ...c, a: c.a + 1 })),
        options: {
          meta: {
            name: 'Action A',
            description: 'First action (respects toggle)',
          },
        },
      },
      {
        hotkey: 'Alt+K',
        callback: () => setCounts((c) => ({ ...c, b: c.b + 1 })),
        options: {
          meta: {
            name: 'Action B',
            description: 'Second action (respects toggle)',
          },
        },
      },
      {
        hotkey: 'Alt+L',
        callback: () => setCounts((c) => ({ ...c, c: c.c + 1 })),
        options: {
          enabled: true,
          meta: {
            name: 'Action C',
            description: 'Always-on action (overrides toggle)',
          },
        },
      },
    ],
    () => ({ enabled: enabled() }),
  )

  return (
    <div class="demo-section">
      <h2>Common Options with Per-Hotkey Overrides</h2>
      <p>
        <kbd>{formatForDisplay('Alt+J' as Hotkey)}</kbd> and{' '}
        <kbd>{formatForDisplay('Alt+K' as Hotkey)}</kbd> respect the global
        toggle. <kbd>{formatForDisplay('Alt+L' as Hotkey)}</kbd> overrides{' '}
        <code>enabled: true</code> so it always works.
      </p>
      <div style={{ 'margin-bottom': '12px' }}>
        <button onClick={() => setEnabled((e) => !e)}>
          {enabled() ? 'Disable' : 'Enable'} common hotkeys
        </button>
      </div>
      <div class="hotkey-grid">
        <div>
          <kbd>{formatForDisplay('Alt+J' as Hotkey)}</kbd> Action A (
          {counts().a})
        </div>
        <div>
          <kbd>{formatForDisplay('Alt+K' as Hotkey)}</kbd> Action B (
          {counts().b})
        </div>
        <div>
          <kbd>{formatForDisplay('Alt+L' as Hotkey)}</kbd> Action C (
          {counts().c})<span class="hint"> (always on)</span>
        </div>
      </div>
      <pre class="code-block">{`createHotkeys(
  [
    { hotkey: 'Alt+J', callback: () => actionA(),
      options: { meta: { name: 'Action A' } } },
    { hotkey: 'Alt+L', callback: () => actionC(),
      options: { enabled: true, meta: { name: 'Action C' } } },
  ],
  () => ({ enabled: enabled() }), // common option
)`}</pre>
    </div>
  )
}

interface DynamicShortcut {
  id: number
  hotkey: string
  label: string
  description: string
  count: number
}

let nextId = 0

const DEFAULT_SHORTCUTS: Array<DynamicShortcut> = [
  {
    id: nextId++,
    hotkey: 'Shift+A',
    label: 'Action A',
    description: 'First dynamic action',
    count: 0,
  },
  {
    id: nextId++,
    hotkey: 'Shift+B',
    label: 'Action B',
    description: 'Second dynamic action',
    count: 0,
  },
  {
    id: nextId++,
    hotkey: 'Shift+C',
    label: 'Action C',
    description: 'Third dynamic action',
    count: 0,
  },
]

function DynamicHotkeysDemo() {
  const [shortcuts, setShortcuts] =
    createSignal<Array<DynamicShortcut>>(DEFAULT_SHORTCUTS)
  const [newHotkey, setNewHotkey] = createSignal('')
  const [newLabel, setNewLabel] = createSignal('')
  const [newDescription, setNewDescription] = createSignal('')

  createHotkeys(() =>
    shortcuts().map(
      (s): CreateHotkeyDefinition => ({
        hotkey: s.hotkey as Hotkey,
        callback: () => {
          setShortcuts((prev) =>
            prev.map((item) =>
              item.id === s.id ? { ...item, count: item.count + 1 } : item,
            ),
          )
        },
        options: {
          meta: { name: s.label, description: s.description },
        },
      }),
    ),
  )

  const addShortcut = () => {
    const trimmed = newHotkey().trim()
    if (!trimmed || !newLabel().trim()) return
    setShortcuts((prev) => [
      ...prev,
      {
        id: nextId++,
        hotkey: trimmed,
        label: newLabel().trim(),
        description: newDescription().trim(),
        count: 0,
      },
    ])
    setNewHotkey('')
    setNewLabel('')
    setNewDescription('')
  }

  const removeShortcut = (id: number) => {
    setShortcuts((prev) => prev.filter((s) => s.id !== id))
  }

  return (
    <div class="demo-section">
      <h2>Dynamic Hotkey List</h2>
      <p>
        Add or remove hotkeys at runtime. Because <code>createHotkeys</code>{' '}
        accepts a dynamic array, this works with Solid's reactivity.
      </p>
      <div class="dynamic-list">
        <For each={shortcuts()}>
          {(s) => (
            <div class="dynamic-item">
              <kbd>{formatForDisplay(s.hotkey as Hotkey)}</kbd>
              <span>{s.label}</span>
              <span class="count">{s.count}</span>
              <button onClick={() => removeShortcut(s.id)}>Remove</button>
            </div>
          )}
        </For>
        <Show when={shortcuts().length === 0}>
          <p class="hint">No shortcuts registered. Add one below.</p>
        </Show>
      </div>
      <div class="add-form">
        <input
          type="text"
          placeholder="Hotkey (e.g. Shift+D)"
          value={newHotkey()}
          onInput={(e) => setNewHotkey(e.currentTarget.value)}
          onKeyDown={(e) => {
            if (e.key === 'Enter') addShortcut()
          }}
        />
        <input
          type="text"
          placeholder="Name (e.g. Action D)"
          value={newLabel()}
          onInput={(e) => setNewLabel(e.currentTarget.value)}
          onKeyDown={(e) => {
            if (e.key === 'Enter') addShortcut()
          }}
        />
        <input
          type="text"
          placeholder="Description (optional)"
          value={newDescription()}
          onInput={(e) => setNewDescription(e.currentTarget.value)}
          onKeyDown={(e) => {
            if (e.key === 'Enter') addShortcut()
          }}
        />
        <button onClick={addShortcut} disabled={!newHotkey() || !newLabel()}>
          Add
        </button>
      </div>
      <pre class="code-block">{`const [shortcuts] = createSignal([...])

createHotkeys(
  () => shortcuts().map((s) => ({
    hotkey: s.hotkey,
    callback: s.action,
    options: { meta: { name: s.name, description: s.description } },
  })),
)`}</pre>
    </div>
  )
}

// ---------------------------------------------------------------------------
// Live registrations viewer using createHotkeyRegistrations
// ---------------------------------------------------------------------------

function RegistrationsViewer() {
  const { hotkeys, sequences } = createHotkeyRegistrations()

  return (
    <div class="demo-section">
      <h2>Live Registrations (createHotkeyRegistrations)</h2>
      <p>
        This table is rendered from <code>createHotkeyRegistrations()</code> — a
        reactive view of all registered hotkeys. It updates automatically as
        hotkeys are added, removed, enabled/disabled, or triggered.
      </p>
      <table class="registrations-table">
        <thead>
          <tr>
            <th>Hotkey</th>
            <th>Name</th>
            <th>Description</th>
            <th>Enabled</th>
            <th>Triggers</th>
          </tr>
        </thead>
        <tbody>
          <For each={hotkeys()}>
            {(reg) => (
              <tr>
                <td>
                  <kbd>{formatForDisplay(reg.hotkey)}</kbd>
                </td>
                <td>{reg.options.meta?.name ?? '\u2014'}</td>
                <td class="description-cell">
                  {reg.options.meta?.description ?? '\u2014'}
                </td>
                <td>
                  <span
                    class={
                      reg.options.enabled !== false ? 'status-on' : 'status-off'
                    }
                  >
                    {reg.options.enabled !== false ? 'yes' : 'no'}
                  </span>
                </td>
                <td class="trigger-count">{reg.triggerCount}</td>
              </tr>
            )}
          </For>
          <Show when={hotkeys().length === 0}>
            <tr>
              <td colSpan={5} class="hint">
                No hotkeys registered
              </td>
            </tr>
          </Show>
        </tbody>
      </table>
      <Show when={sequences().length > 0}>
        <h3 style={{ 'margin-top': '16px' }}>Sequences</h3>
        <table class="registrations-table">
          <thead>
            <tr>
              <th>Sequence</th>
              <th>Name</th>
              <th>Description</th>
              <th>Triggers</th>
            </tr>
          </thead>
          <tbody>
            <For each={sequences()}>
              {(reg) => (
                <tr>
                  <td>
                    <For each={reg.sequence}>
                      {(s, i) => (
                        <>
                          {i() > 0 && ' '}
                          <kbd>{formatForDisplay(s)}</kbd>
                        </>
                      )}
                    </For>
                  </td>
                  <td>{reg.options.meta?.name ?? '\u2014'}</td>
                  <td class="description-cell">
                    {reg.options.meta?.description ?? '\u2014'}
                  </td>
                  <td class="trigger-count">{reg.triggerCount}</td>
                </tr>
              )}
            </For>
          </tbody>
        </table>
      </Show>
      <pre class="code-block">{`const { hotkeys, sequences } = createHotkeyRegistrations()

// Render a live table of all registrations
<For each={hotkeys()}>
  {(reg) => (
    <tr>
      <td>{formatForDisplay(reg.hotkey)}</td>
      <td>{reg.options.meta?.name}</td>
      <td>{reg.options.meta?.description}</td>
      <td>{reg.triggerCount}</td>
    </tr>
  )}
</For>`}</pre>
    </div>
  )
}

function App() {
  return (
    <>
      <HotkeysProvider>
        <div class="app">
          <header>
            <h1>createHotkeys</h1>
            <p>
              Register multiple hotkeys in a single primitive call. Supports
              dynamic arrays for variable-length shortcut lists.
            </p>
          </header>
          <BasicMultiHotkeys />
          <CommonOptionsDemo />
          <DynamicHotkeysDemo />
          <RegistrationsViewer />
        </div>
      </HotkeysProvider>
      <TanStackDevtools plugins={plugins} />
    </>
  )
}

const rootElement = document.getElementById('root')!
render(() => <App />, rootElement)