ShortcutListener
Register page-specific shortcuts that only work on specific pages or components.
Basic Usage
Use ShortcutListener to register shortcuts that are specific to a page or component:
import { ShortcutListener } from '@chrisnski/webshorts';
function MyPage() {
const handleSave = () => {
console.log('Saving...');
};
return (
<div>
<h1>My Page</h1>
{/* Register shortcuts for this page */}
<ShortcutListener keys='CTRL + S' action={handleSave} />
<ShortcutListener keys='ALT + N' action={() => console.log('New item')} />
{/* Your page content */}
</div>
);
}
Props Reference
The ShortcutListener component accepts several props to customize its behavior:
<ShortcutListener
keys='CTRL + S'
action={handleSave}
page='/dashboard' // Optional, defaults to current page
/>
Required Props
keys
Key combination (e.g., "CTRL + SHIFT + A")
action
Function to execute when shortcut is pressed
Optional Props
page
Page where this shortcut is active (defaults to current page)
children
Optional children for semantic grouping (not required for functionality)
Usage Examples
Basic Shortcut
// Simple shortcut with function
<ShortcutListener
keys='CTRL + S'
action={() => saveDocument()}
/>
With Specific Page
// Shortcut that only works on a specific page
<ShortcutListener
keys='CTRL + B'
action={handleBold}
page='/editor'
/>
Multiple Shortcuts
// Register multiple shortcuts in one component
function EditorPage() {
return (
<div>
<ShortcutListener keys='CTRL + S' action={handleSave} />
<ShortcutListener keys='CTRL + Z' action={handleUndo} />
<ShortcutListener keys='CTRL + Y' action={handleRedo} />
<ShortcutListener keys='CTRL + B' action={handleBold} />
<ShortcutListener keys='CTRL + I' action={handleItalic} />
{/* Editor content */}
</div>
);
}
With Children (Optional)
// ShortcutListener can wrap children for semantic grouping
// Children are optional and don't affect functionality
<ShortcutListener keys='ESC' action={handleEscape}>
<div className='editor-toolbar'>
{/* Toolbar content */}
</div>
</ShortcutListener>
// Or without children (functionally identical)
<ShortcutListener keys='ESC' action={handleEscape} />
<div className='editor-toolbar'>
{/* Toolbar content */}
</div>
Children Functionality
ShortcutListener can optionally wrap children for semantic grouping, but this is purely for code organization and readability. The children don't affect the shortcut functionality in any way.
Semantic Grouping
- • Optional - Children are not required for shortcut functionality
- • Semantic - Useful for grouping related UI elements with their shortcuts
- • Readability - Makes code more self-documenting
- • No Impact - Children don't affect shortcut behavior or registration
// These two approaches are functionally identical:
// With children (semantic grouping)
<ShortcutListener keys='CTRL + S' action={handleSave}>
<button className='save-button'>Save</button>
</ShortcutListener>
// Without children (same functionality)
<ShortcutListener keys='CTRL + S' action={handleSave} />
<button className='save-button'>Save</button>
Component Lifecycle
ShortcutListener automatically handles registration and cleanup of shortcuts:
function MyComponent() {
const [isEditing, setIsEditing] = useState(false);
return (
<div>
{/* This shortcut is registered when component mounts */}
<ShortcutListener
keys='CTRL + S'
action={handleSave}
/>
{/* This shortcut is only registered when isEditing is true */}
{isEditing && (
<ShortcutListener
keys='ESC'
action={() => setIsEditing(false)}
/>
)}
</div>
);
}
Automatic Cleanup
- • Registration - Shortcuts are registered when the component mounts
- • Cleanup - Shortcuts are automatically unregistered when the component unmounts
- • Conditional - Shortcuts can be conditionally registered based on component state
- • Memory safe - No memory leaks from forgotten event listeners
Common Patterns
Form Shortcuts
function ContactForm() {
return (
<form>
<ShortcutListener keys='CTRL + ENTER' action={handleSubmit} />
<ShortcutListener keys='ESC' action={handleCancel} />
{/* Form fields */}
</form>
);
}
Button Grouping (Semantic)
function Toolbar() {
return (
<div className='toolbar'>
<ShortcutListener keys='CTRL + S' action={handleSave}>
<button className='toolbar-button'>Save</button>
</ShortcutListener>
<ShortcutListener keys='CTRL + Z' action={handleUndo}>
<button className='toolbar-button'>Undo</button>
</ShortcutListener>
<ShortcutListener keys='CTRL + Y' action={handleRedo}>
<button className='toolbar-button'>Redo</button>
</ShortcutListener>
</div>
);
}
Editor Shortcuts
function TextEditor() {
const [isEditing, setIsEditing] = useState(false);
return (
<div>
<ShortcutListener keys='CTRL + E' action={() => setIsEditing(!isEditing)} />
{isEditing && (
<>
<ShortcutListener keys='CTRL + S' action={handleSave} />
<ShortcutListener keys='CTRL + Z' action={handleUndo} />
<ShortcutListener keys='ESC' action={() => setIsEditing(false)} />
</>
)}
{/* Editor content */}
</div>
);
}
Navigation Shortcuts
function DashboardPage() {
return (
<div>
<ShortcutListener keys='ALT + 1' action={() => navigate('/dashboard')} />
<ShortcutListener keys='ALT + 2' action={() => navigate('/profile')} />
<ShortcutListener keys='ALT + 3' action={() => navigate('/settings')} />
{/* Dashboard content */}
</div>
);
}
Troubleshooting
Shortcut not working?
- • Check that the component is mounted and rendered
- • Verify the key combination format (e.g., "CTRL + S" not "Ctrl + S")
- • Ensure the action function is properly defined
- • Check for conflicts with other shortcuts or browser defaults
- • Enable debug mode to see registration feedback
Shortcut conflicts?
- • Check for duplicate shortcuts on the same page
- • Verify that the page prop is correctly set
- • Ensure conditional rendering logic is correct
- • Test with debug mode enabled to see all registered shortcuts