Giới thiệu TanStack Hotkeys
TanStack Hotkeys là thư viện giúp bạn thêm phím tắt bàn phím vào ứng dụng web. Bạn dùng nó khi dashboard, editor, command palette hoặc trang quản trị cần thao tác nhanh bằng bàn phím.
TanStack Hotkeys phù hợp với React và Next.js vì thư viện cung cấp hook như useHotkey, useHotkeySequence, useHotkeyRecorder, useKeyHold và các hàm hiển thị shortcut như formatForDisplay.
TanStack Hotkeys đang được ghi nhãn alpha trên tài liệu chính thức. Nếu dùng cho production, hãy kiểm tra kỹ hành vi phím tắt, khả năng truy cập và thay đổi API khi nâng phiên bản.
Khi nào nên dùng
| Trường hợp | Ví dụ |
|---|
| Lưu nhanh dữ liệu | Mod+S để lưu form hoặc tài liệu |
| Mở command palette | Mod+K để mở thanh lệnh |
| Đóng dialog hoặc panel | Escape để đóng modal |
| Điều hướng kiểu editor | G rồi G để về đầu trang |
| Tùy chỉnh shortcut | Cho người dùng tự ghi phím tắt trong trang settings |
| Hiển thị trạng thái phím | Giữ Shift để đổi hành động xóa |
Tính năng chính
| Tính năng | Công dụng |
|---|
| Type-safe hotkey strings | Khai báo shortcut bằng chuỗi như Mod+S hoặc Escape |
Cross-platform Mod | Tự dùng Command trên macOS và Control trên Windows/Linux |
| Keyboard sequences | Bắt chuỗi phím bấm liên tiếp như G, G |
| Hotkey recording | Cho người dùng ghi lại shortcut của riêng họ |
| Key state tracking | Biết phím nào đang được giữ |
| Scope theo element | Chỉ kích hoạt shortcut trong một vùng giao diện |
| Display formatting | Hiển thị shortcut đúng theo hệ điều hành |
| Cleanup tự động | Hook tự dọn listener khi component unmount |
Cài đặt
Cài package React
Chạy lệnh sau trong dự án Next.js:npm install @tanstack/react-hotkeys
Dùng trong client component
Các hook của TanStack Hotkeys cần chạy trong browser, nên component phải có directive use client.
Ví dụ cơ bản
Ví dụ dưới đây thêm shortcut Mod+S. Trên macOS, người dùng bấm Command+S. Trên Windows/Linux, người dùng bấm Control+S.
'use client'
import { formatForDisplay, useHotkey } from '@tanstack/react-hotkeys'
type SaveShortcutProps = {
onSave: () => void
}
export function SaveShortcut({ onSave }: SaveShortcutProps) {
useHotkey('Mod+S', () => {
onSave()
})
return (
<button type="button" onClick={onSave}>
Lưu <kbd>{formatForDisplay('Mod+S')}</kbd>
</button>
)
}
useHotkey mặc định gọi preventDefault và stopPropagation. Vì vậy Mod+S sẽ chạy hàm onSave thay vì mở hộp thoại lưu trang của trình duyệt.
Scope shortcut theo vùng giao diện
Khi shortcut chỉ nên chạy trong một panel hoặc form cụ thể, truyền target bằng ref.
'use client'
import { useRef } from 'react'
import { useHotkey } from '@tanstack/react-hotkeys'
type CustomerPanelProps = {
onClose: () => void
}
export function CustomerPanel({ onClose }: CustomerPanelProps) {
const panelRef = useRef<HTMLDivElement>(null)
useHotkey('Escape', onClose, {
target: panelRef,
})
return (
<div ref={panelRef} tabIndex={0}>
<h2>Chi tiết khách hàng</h2>
<p>Bấm Escape khi panel đang focus để đóng.</p>
</div>
)
}
Bật tắt shortcut theo trạng thái
Dùng enabled khi shortcut chỉ hợp lệ trong một trạng thái nhất định.
'use client'
import { useHotkey } from '@tanstack/react-hotkeys'
type DeleteDialogProps = {
open: boolean
onConfirm: () => void
onClose: () => void
}
export function DeleteDialog({ open, onConfirm, onClose }: DeleteDialogProps) {
useHotkey('Enter', onConfirm, { enabled: open })
useHotkey('Escape', onClose, { enabled: open })
if (!open) {
return null
}
return (
<div role="dialog" aria-modal="true">
<p>Bạn có chắc muốn xóa bản ghi này?</p>
<button type="button" onClick={onConfirm}>
Xóa
</button>
<button type="button" onClick={onClose}>
Hủy
</button>
</div>
)
}
Dùng chuỗi phím
useHotkeySequence dùng cho các lệnh cần nhiều bước, ví dụ phím tắt kiểu Vim.
'use client'
import { useHotkeySequence } from '@tanstack/react-hotkeys'
export function PageShortcuts() {
useHotkeySequence(['G', 'G'], () => {
window.scrollTo({ top: 0, behavior: 'smooth' })
})
useHotkeySequence(['G', 'Shift+G'], () => {
window.scrollTo({ top: document.body.scrollHeight, behavior: 'smooth' })
})
return null
}
Người dùng phải bấm đúng thứ tự trong khoảng thời gian cho phép. Bạn có thể truyền timeout nếu muốn rút ngắn hoặc kéo dài thời gian chờ.
useHotkeySequence(['D', 'D'], deleteSelectedItem, {
timeout: 500,
})
Ghi shortcut do người dùng chọn
useHotkeyRecorder phù hợp cho trang settings, nơi người dùng tự chọn shortcut.
'use client'
import { formatForDisplay, useHotkeyRecorder } from '@tanstack/react-hotkeys'
type ShortcutRecorderProps = {
onChange: (hotkey: string) => void
}
export function ShortcutRecorder({ onChange }: ShortcutRecorderProps) {
const recorder = useHotkeyRecorder({
onRecord: onChange,
})
return (
<button
type="button"
onClick={recorder.isRecording ? recorder.stopRecording : recorder.startRecording}
>
{recorder.isRecording
? 'Bấm tổ hợp phím...'
: recorder.recordedHotkey
? formatForDisplay(recorder.recordedHotkey)
: 'Ghi shortcut'}
</button>
)
}
Theo dõi phím đang được giữ
useKeyHold giúp UI phản hồi khi người dùng đang giữ một phím cụ thể.
'use client'
import { useKeyHold } from '@tanstack/react-hotkeys'
type DeleteButtonProps = {
onMoveToTrash: () => void
onDeleteForever: () => void
}
export function DeleteButton({ onMoveToTrash, onDeleteForever }: DeleteButtonProps) {
const isShiftHeld = useKeyHold('Shift')
return (
<button
type="button"
onClick={isShiftHeld ? onDeleteForever : onMoveToTrash}
>
{isShiftHeld ? 'Xóa vĩnh viễn' : 'Chuyển vào thùng rác'}
</button>
)
}
Lưu ý khi dùng trong Next.js
- Chỉ gọi hook trong client component.
- Dùng
Mod thay vì tự tách Control và Meta.
- Không ghi đè shortcut quen thuộc của trình duyệt nếu không có lý do rõ ràng.
- Luôn cho người dùng thấy shortcut bằng
formatForDisplay.
- Kiểm tra shortcut khi focus đang nằm trong
input, textarea, select hoặc vùng contentEditable.
- Với dialog, modal và panel, ưu tiên dùng
enabled hoặc target để giới hạn phạm vi.
Với dashboard quản trị, hãy bắt đầu bằng các shortcut ít rủi ro như Mod+K, Mod+S, Escape, rồi mới thêm các shortcut một phím như G hoặc D.
Prompt gợi ý cho Claude Code
Thêm TanStack Hotkeys vào dự án Next.js hiện tại.
Tạo các shortcut sau:
- Mod+K mở command palette.
- Mod+S lưu form hiện tại.
- Escape đóng dialog đang mở.
Yêu cầu:
- Dùng @tanstack/react-hotkeys.
- Các hook phải nằm trong client component.
- Hiển thị shortcut bằng formatForDisplay.
- Không làm shortcut chạy khi đang nhập liệu nếu hành vi đó gây lỗi UX.
Tài liệu tham khảo