MultiSelect
MultiSelect is the ordinary multi-select list component. Like Select, it owns an internal ListState, but its interaction has two layers: Space toggles draft selection and triggers on_change; Enter submits the current selection and triggers on_select.

This recording is generated by docs/tapes/multi-select.tape. The example uses default_index to put the cursor on the first item, checks Format with Space, moves to Clippy, checks it, presses Enter to submit, and finally switches to the empty state.
Press j/k to move, Space to toggle selection, Enter to submit, e to toggle the empty state, and q to quit.
Minimal form
Section titled “Minimal form”default_index only controls the default cursor and does not automatically check an item. The selected set always comes from the user’s Space actions or from the external selected state you pass in.
Draft and submit
Section titled “Draft and submit”Both on_change and on_select receive a Vec<T> already ordered by current item index:
on_change: triggered after everySpace; useful for updating a preview panel, counter, or filter summary.on_select: triggered afterEnter; useful for confirming submit, closing a modal, or entering the next step.
This split fits search results, batch operations, settings panels, and multi-select confirmations in modals: users can try a draft selection, then submit with an explicit action.
External state
Section titled “External state”By default, MultiSelect stores its own cursor and selected set. If business code needs to read or reset them across components, pass external state:
selected is typed as State<HashSet<usize>>. When the list becomes shorter, the component automatically removes selected indices that are out of bounds; when the cursor is out of bounds, it clamps the cursor back to the last valid item. An empty list renders only empty_message and does not consume navigation or confirm keys, so the parent can still handle page shortcuts like e and q.
Layout and style
Section titled “Layout and style”MultiSelect can receive width, height, margin, and offset, and forwards them to its internal Border:
If a page has multiple selection controls, set active: false on the instance that should not currently respond to input. When placed inside a Modal subtree, the Current handler automatically belongs to the modal input layer, so components behind the modal do not process the same keys. For hierarchical data, see TreeSelect.