Input
Examples
Worked examples for common input patterns
File manager with search
A filterable file list composed from Select and TextInput. Because each component owns its keybindings, the parent scope has no navigation or character-capture logic — it only routes focus between fields. / bubbles up from the Select (which doesn't consume it) and triggers focusChild('search'); escape from the TextInput (which ignores it) triggers the reverse:
function FileManager() {
const [query, setQuery] = useState('');
const options = FILES.filter(
f => !query || f.value.toLowerCase().includes(query.toLowerCase())
);
const scope = useFocusScope({
keybindings: ({ focusChild, next, prev }) => ({
'/': () => focusChild('search'),
escape: () => { setQuery(''); focusChild('list'); },
tab: next,
'shift+tab': prev,
}),
});
return (
<Box flexDirection="column" gap={1}>
<FocusScope handle={scope}>
<TextInput
focusKey="search"
label="/"
value={query}
onChange={setQuery}
onSubmit={() => scope.focusChild('list')}
placeholder="filter files…"
/>
<Select focusKey="list" options={options} onSubmit={open} />
</FocusScope>
</Box>
);
}Modal with focus trap
A confirmation dialog that completely blocks input to the menu behind it. Without FocusTrap, j/k on the menu scope would still fire while the dialog is open — the trap prevents that by stopping all key bubbling at the modal boundary:
function ConfirmDialog({ message, onConfirm, onCancel }) {
const scope = useFocusScope({
keybindings: ({ next, prev }) => ({
left: prev,
right: next,
escape: onCancel,
})
});
return (
<Box flexDirection="column" borderStyle="double">
<Text bold color="yellow">Confirm Action</Text>
<Text>{message}</Text>
<FocusScope handle={scope}>
<MenuItem label="Confirm" onSelect={onConfirm} />
<MenuItem label="Cancel" onSelect={onCancel} />
</FocusScope>
</Box>
);
}
function App() {
const [showModal, setShowModal] = useState(false);
const menuScope = useFocusScope({
keybindings: ({ next, prev }) => ({ j: next, k: prev })
});
return (
<Box flexDirection="column">
<FocusScope handle={menuScope}>
<MenuItem label="New File" onSelect={() => setShowModal(true)} />
<MenuItem label="Delete" onSelect={() => setShowModal(true)} />
</FocusScope>
{showModal && (
<FocusTrap>
<ConfirmDialog
message="Are you sure?"
onConfirm={() => setShowModal(false)}
onCancel={() => setShowModal(false)}
/>
</FocusTrap>
)}
</Box>
);
}