diff --git a/package.json b/package.json
index 8fb81cd1..0728b69c 100644
--- a/package.json
+++ b/package.json
@@ -50,6 +50,7 @@
"@tailwindcss/vite": "^4.1.11",
"@tanstack/create": "^0.49.1",
"@tanstack/pacer": "^0.16.4",
+ "@tanstack/react-hotkeys": "^0.0.2",
"@tanstack/react-pacer": "^0.17.4",
"@tanstack/react-query": "^5.90.12",
"@tanstack/react-router": "1.157.16",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 114304ce..4e5892f8 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -89,6 +89,9 @@ importers:
'@tanstack/pacer':
specifier: ^0.16.4
version: 0.16.4
+ '@tanstack/react-hotkeys':
+ specifier: ^0.0.2
+ version: 0.0.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
'@tanstack/react-pacer':
specifier: ^0.17.4
version: 0.17.4(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
@@ -3137,6 +3140,10 @@ packages:
resolution: {integrity: sha512-xyIfof8eHBuub1CkBnbKNKQXeRZC4dClhmzePHVOEel4G7lk/dW+TQ16da7CFdeNLv6u6Owf5VoBQxoo6DFTSA==}
engines: {node: '>=12'}
+ '@tanstack/hotkeys@0.0.2':
+ resolution: {integrity: sha512-HUki67sfc6z62iIY+ito7HV4+cFmsHlwLOWdFN8Aomgw7HJH8zHtBSksVw9Gh7qSapKi29fWa3DViPMzUawN2Q==}
+ engines: {node: '>=18'}
+
'@tanstack/pacer@0.16.4':
resolution: {integrity: sha512-dqd6p1JK6iucOhJSOA1/VCvT46kZDoem/l/xcYtQpG4Ygxl8xzSW69oMk0bTSh+cAvFXDCrXn3wlS7Otir/fsA==}
engines: {node: '>=18'}
@@ -3144,6 +3151,13 @@ packages:
'@tanstack/query-core@5.90.12':
resolution: {integrity: sha512-T1/8t5DhV/SisWjDnaiU2drl6ySvsHj1bHBCWNXd+/T+Hh1cf6JodyEYMd5sgwm+b/mETT4EV3H+zCVczCU5hg==}
+ '@tanstack/react-hotkeys@0.0.2':
+ resolution: {integrity: sha512-LaW28h7omiIWgyw61gEU2k2X4YlesB8Gptvw71si77cXbg7RphD+Qu+q/mA84ai4x2miLA6vvYuZTvIVcbz9tw==}
+ engines: {node: '>=18'}
+ peerDependencies:
+ react: '>=16.8'
+ react-dom: '>=16.8'
+
'@tanstack/react-pacer@0.17.4':
resolution: {integrity: sha512-VdHuN+FkdKwPMD2uuO0qb04CBMOci68CeciQCTASA5Tmts9uxiSHIJEM+ABh/s4pSEdMKM/GI6sFWDNwwCf6yA==}
engines: {node: '>=18'}
@@ -11295,6 +11309,10 @@ snapshots:
'@tanstack/history@1.154.14': {}
+ '@tanstack/hotkeys@0.0.2':
+ dependencies:
+ '@tanstack/store': 0.8.0
+
'@tanstack/pacer@0.16.4':
dependencies:
'@tanstack/devtools-event-client': 0.3.5
@@ -11302,6 +11320,13 @@ snapshots:
'@tanstack/query-core@5.90.12': {}
+ '@tanstack/react-hotkeys@0.0.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3)':
+ dependencies:
+ '@tanstack/hotkeys': 0.0.2
+ '@tanstack/react-store': 0.8.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
+ react: 19.2.3
+ react-dom: 19.2.3(react@19.2.3)
+
'@tanstack/react-pacer@0.17.4(react-dom@19.2.3(react@19.2.3))(react@19.2.3)':
dependencies:
'@tanstack/pacer': 0.16.4
diff --git a/src/components/LibraryHero.tsx b/src/components/LibraryHero.tsx
index e1600512..e0e5c513 100644
--- a/src/components/LibraryHero.tsx
+++ b/src/components/LibraryHero.tsx
@@ -8,7 +8,7 @@ type LibraryHeroProps = {
project: Library
cta?: {
linkProps: LinkProps
- label: string
+ label: string | React.ReactNode
className?: string
}
actions?: React.ReactNode
diff --git a/src/components/landing/HotkeysLanding.tsx b/src/components/landing/HotkeysLanding.tsx
new file mode 100644
index 00000000..c569d9f0
--- /dev/null
+++ b/src/components/landing/HotkeysLanding.tsx
@@ -0,0 +1,93 @@
+import { Footer } from '~/components/Footer'
+import { LibraryHero } from '~/components/LibraryHero'
+import { PartnersSection } from '~/components/PartnersSection'
+import { MaintainersSection } from '~/components/MaintainersSection'
+import { LazySponsorSection } from '~/components/LazySponsorSection'
+import { BottomCTA } from '~/components/BottomCTA'
+import { hotkeysProject } from '~/libraries/hotkeys'
+import { getLibrary } from '~/libraries'
+import { LibraryFeatureHighlights } from '~/components/LibraryFeatureHighlights'
+import LandingPageGad from '~/components/LandingPageGad'
+import { LibraryPageContainer } from '~/components/LibraryPageContainer'
+import { LibraryStatsSection } from '~/components/LibraryStatsSection'
+import { FeatureGridSection } from '~/components/FeatureGridSection'
+import { formatForDisplay, useHotkey } from '@tanstack/react-hotkeys'
+import { useNavigate } from '@tanstack/react-router'
+
+const library = getLibrary('hotkeys')
+
+export default function HotkeysLanding() {
+ const navigate = useNavigate()
+
+ useHotkey('Mod+Enter', () => {
+ navigate({
+ to: '/$libraryId/$version/docs',
+ params: { libraryId: library.id, version: 'latest' },
+ })
+ })
+
+ return (
+
+
+ Get Started{' '}
+ {formatForDisplay('Mod+Enter')}
+ >
+ ),
+ className: 'bg-rose-600 border-rose-600 hover:bg-rose-700 text-white',
+ }}
+ />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
diff --git a/src/libraries/hotkeys.tsx b/src/libraries/hotkeys.tsx
new file mode 100644
index 00000000..bc595b94
--- /dev/null
+++ b/src/libraries/hotkeys.tsx
@@ -0,0 +1,65 @@
+import { Keyboard, ListOrdered, WandSparkles } from 'lucide-react'
+import { twMerge } from 'tailwind-merge'
+import { hotkeys } from './libraries'
+
+const textStyles = 'text-rose-600 dark:text-rose-500'
+
+export const hotkeysProject = {
+ ...hotkeys,
+ description: `A type-safe, cross-platform hotkey library with sequence detection, key state tracking, hotkey recording, and framework adapters for React and more.`,
+ ogImage: 'https://github.com/tanstack/hotkeys/raw/main/media/repo-header.png',
+ latestBranch: 'main',
+ bgRadial: 'from-rose-500 via-rose-700/50 to-transparent',
+ textColor: 'text-rose-700',
+ defaultDocs: 'overview',
+ featureHighlights: [
+ {
+ title: 'Type-Safe & Cross-Platform',
+ icon: ,
+ description: (
+
+ Define keyboard shortcuts with a{' '}
+
+ fully type-safe Hotkey string type
+ {' '}
+ that validates key combinations at the type level. Cross-platform{' '}
+ Mod modifier automatically maps to Cmd on macOS and Ctrl
+ elsewhere, so your shortcuts work everywhere without platform checks.
+
+ ),
+ },
+ {
+ title: 'Better Defaults',
+ icon: ,
+ description: (
+
+ Ships with{' '}
+
+ sensible and safe defaults
+
+ , including automatic preventDefault and{' '}
+ stopPropagation, smartly ignoring shortcuts when input
+ fields are focused, and automatic cleanup on unmount. Scoping hotkeys
+ to refs or elements is easy, making it straightforward to define
+ context-aware keyboard shortcuts without unexpected side-effects.
+
+ ),
+ },
+ {
+ title: 'Sequences & Recording',
+ icon: ,
+ description: (
+
+ Build{' '}
+
+ multi-step keyboard sequences
+ {' '}
+ like Vim-style commands or cheat codes with configurable timeouts. Let
+ users record and customize their own shortcuts with the built-in
+ hotkey recorder that captures modifier and key combinations in real
+ time.
+
+ ),
+ },
+ ],
+}
diff --git a/src/libraries/index.tsx b/src/libraries/index.tsx
index f6ecdad6..314dab78 100644
--- a/src/libraries/index.tsx
+++ b/src/libraries/index.tsx
@@ -18,6 +18,7 @@ export {
ranger,
store,
pacer,
+ hotkeys,
db,
ai,
config,
diff --git a/src/libraries/libraries.ts b/src/libraries/libraries.ts
index 813bee09..6c543f7d 100644
--- a/src/libraries/libraries.ts
+++ b/src/libraries/libraries.ts
@@ -271,6 +271,33 @@ export const pacer: LibrarySlim = {
defaultDocs: 'overview',
}
+export const hotkeys: LibrarySlim = {
+ id: 'hotkeys',
+ name: 'TanStack Hotkeys',
+ cardStyles: 'text-rose-500 dark:text-rose-400 hover:border-current',
+ to: '/hotkeys',
+ tagline:
+ 'Type-safe keyboard shortcuts, sequences, and key state tracking for your apps',
+ description:
+ 'A type-safe, cross-platform hotkey library with sequence detection, key state tracking, hotkey recording, and framework adapters for React and more.',
+ badge: 'alpha',
+ bgStyle: 'bg-rose-500',
+ borderStyle: 'border-rose-500/50',
+ textStyle: 'text-rose-500 dark:text-rose-400',
+ textColor: 'text-rose-600 dark:text-rose-400',
+ colorFrom: 'from-rose-500',
+ colorTo: 'to-rose-700',
+ bgRadial: 'from-rose-500 via-rose-700/50 to-transparent',
+ repo: 'tanstack/hotkeys',
+ frameworks: ['react'],
+ corePackageName: '@tanstack/hotkeys',
+ latestVersion: 'v0',
+ latestBranch: 'main',
+ availableVersions: ['v0'],
+ ogImage: 'https://github.com/tanstack/hotkeys/raw/main/media/repo-header.png',
+ defaultDocs: 'overview',
+}
+
export const db: LibrarySlim = {
id: 'db',
name: 'TanStack DB',
@@ -453,6 +480,7 @@ export const libraries: LibrarySlim[] = [
ai,
virtual,
pacer,
+ hotkeys,
store,
ranger,
config,
@@ -497,14 +525,14 @@ export const libraries: LibrarySlim[] = [
export const librariesByGroup = {
state: [start, router, query, db, store, ai],
- headlessUI: [table, form],
+ headlessUI: [table, form, hotkeys],
performance: [virtual, pacer],
tooling: [devtools, config, cli],
}
export const librariesGroupNamesMap = {
- state: 'Data and State Management',
- headlessUI: 'Headless UI',
+ state: 'Data & State Management',
+ headlessUI: 'UI & UX',
performance: 'Performance',
tooling: 'Tooling',
}
@@ -535,6 +563,7 @@ export const SIDEBAR_LIBRARY_IDS = [
'form',
'virtual',
'pacer',
+ 'hotkeys',
'store',
'devtools',
'cli',
diff --git a/src/libraries/maintainers.ts b/src/libraries/maintainers.ts
index 53666623..d12208a6 100644
--- a/src/libraries/maintainers.ts
+++ b/src/libraries/maintainers.ts
@@ -97,11 +97,11 @@ export const allMaintainers: Maintainer[] = [
isCoreMaintainer: true,
avatar: 'https://github.com/kevinvandy.png',
github: 'kevinvandy',
- creatorOf: ['pacer'],
+ creatorOf: ['pacer', 'hotkeys'],
maintainerOf: ['table'],
contributorOf: ['virtual'],
consultantOf: ['query'],
- frameworkExpertise: ['react', 'solid', 'svelte'],
+ frameworkExpertise: ['react', 'preact', 'solid'],
specialties: ['Tables', 'Data Grids', 'Dashboards'],
social: {
twitter: 'https://x.com/kevinvancott',
@@ -301,6 +301,7 @@ export const allMaintainers: Maintainer[] = [
'query',
'ranger',
'router',
+ 'hotkeys',
'start',
'store',
'table',
diff --git a/src/libraries/types.ts b/src/libraries/types.ts
index 8aa1e782..74575ce8 100644
--- a/src/libraries/types.ts
+++ b/src/libraries/types.ts
@@ -21,6 +21,7 @@ export type LibraryId =
| 'ranger'
| 'store'
| 'pacer'
+ | 'hotkeys'
| 'db'
| 'ai'
| 'config'
diff --git a/src/routes/$libraryId/$version.index.tsx b/src/routes/$libraryId/$version.index.tsx
index b7830679..6b96c620 100644
--- a/src/routes/$libraryId/$version.index.tsx
+++ b/src/routes/$libraryId/$version.index.tsx
@@ -26,6 +26,7 @@ const landingComponents: Partial<
virtual: React.lazy(() => import('~/components/landing/VirtualLanding')),
ranger: React.lazy(() => import('~/components/landing/RangerLanding')),
pacer: React.lazy(() => import('~/components/landing/PacerLanding')),
+ hotkeys: React.lazy(() => import('~/components/landing/HotkeysLanding')),
config: React.lazy(() => import('~/components/landing/ConfigLanding')),
db: React.lazy(() => import('~/components/landing/DbLanding')),
ai: React.lazy(() => import('~/components/landing/AiLanding')),