aria-multiselectable
Indicates that the user may select more than one item from the current selectable descendants. Essential for accessible multi-select listboxes, grids, and tree views.
Overview
The aria-multiselectable attribute indicates that more than one item can be selected from a group of selectable items. Screen readers announce this as "multi-selectable" to help users understand they can choose multiple options.
This attribute is used on container elements like listbox, grid, tree, and tablist. The individual items within use aria-selected to indicate their selection state.
Keyboard Conventions
Multi-select widgets typically support Ctrl/Cmd + Click for toggling individual items and Shift + Click for range selection. Ctrl/Cmd + A often selects all items.
Live Demo: Multi-Select Listbox
- JavaScript
- TypeScript
- Python
- Go
- Rust
- Java
Selected: 0 of 6 (None)
Screen reader: "Select programming languages, listbox, multi-selectable". When selecting: "JavaScript, selected, 1 of 6". When toggling: "TypeScript, not selected".
Attribute Values
trueMultiple items can be selected simultaneously. Users can select/deselect items independently without clearing previous selections.
false(default)Only one item can be selected at a time. Selecting a new item automatically deselects the previously selected item. This is the default when the attribute is absent.
Code Examples
Multi-Select Listbox
<!-- Multi-select listbox -->
<label id="langs-label">Select programming languages:</label>
<ul
role="listbox"
aria-labelledby="langs-label"
aria-multiselectable="true"
tabindex="0"
>
<li role="option" aria-selected="true">JavaScript</li>
<li role="option" aria-selected="true">TypeScript</li>
<li role="option" aria-selected="false">Python</li>
<li role="option" aria-selected="false">Go</li>
<li role="option" aria-selected="true">Rust</li>
</ul>
<!-- Screen reader: "Select programming languages, listbox, multi-selectable" -->
<!-- Per option: "JavaScript, selected, 1 of 5" -->Single vs Multi-Select
<!-- Comparison: Single vs Multi-select -->
<!-- SINGLE SELECT (default behavior) -->
<ul role="listbox" aria-label="Choose one country">
<!-- aria-multiselectable is absent or "false" -->
<li role="option" aria-selected="false">USA</li>
<li role="option" aria-selected="true">Canada</li>
<li role="option" aria-selected="false">UK</li>
</ul>
<!-- Only ONE option can be selected at a time -->
<!-- MULTI-SELECT -->
<ul
role="listbox"
aria-label="Select countries to visit"
aria-multiselectable="true"
>
<li role="option" aria-selected="true">USA</li>
<li role="option" aria-selected="true">Canada</li>
<li role="option" aria-selected="false">UK</li>
</ul>
<!-- MULTIPLE options can be selected -->Multi-Select Grid
<!-- Multi-select grid (like spreadsheet cells) -->
<table
role="grid"
aria-label="Data spreadsheet"
aria-multiselectable="true"
>
<thead>
<tr>
<th role="columnheader">Name</th>
<th role="columnheader">Email</th>
<th role="columnheader">Role</th>
</tr>
</thead>
<tbody>
<tr role="row" aria-selected="true">
<td role="gridcell">John Doe</td>
<td role="gridcell">john@example.com</td>
<td role="gridcell">Admin</td>
</tr>
<tr role="row" aria-selected="true">
<td role="gridcell">Jane Smith</td>
<td role="gridcell">jane@example.com</td>
<td role="gridcell">User</td>
</tr>
<tr role="row" aria-selected="false">
<td role="gridcell">Bob Wilson</td>
<td role="gridcell">bob@example.com</td>
<td role="gridcell">User</td>
</tr>
</tbody>
</table>
<!-- Use aria-selected on rows for row selection -->
<!-- Use aria-selected on cells for cell selection -->Multi-Select Tree
<!-- Multi-select tree (file browser) -->
<ul
role="tree"
aria-label="File browser"
aria-multiselectable="true"
>
<li
role="treeitem"
aria-selected="false"
aria-expanded="true"
>
π Documents
<ul role="group">
<li role="treeitem" aria-selected="true">π report.pdf</li>
<li role="treeitem" aria-selected="true">π notes.txt</li>
<li role="treeitem" aria-selected="false">π todo.md</li>
</ul>
</li>
<li
role="treeitem"
aria-selected="false"
aria-expanded="false"
>
π Images
</li>
</ul>
<!-- Shift+Click for range selection -->
<!-- Ctrl/Cmd+Click for individual toggle -->React Component
// React Multi-select Listbox
import { useState, useCallback } from 'react';
function MultiSelectListbox({
items,
label,
onChange,
initialSelected = []
}) {
const [selected, setSelected] = useState(new Set(initialSelected));
const [activeIndex, setActiveIndex] = useState(0);
const toggleSelection = useCallback((itemId, shiftKey) => {
setSelected(prev => {
const newSelected = new Set(prev);
if (shiftKey && prev.size > 0) {
// Range selection with Shift
// Implementation depends on use case
} else {
// Toggle single item
if (newSelected.has(itemId)) {
newSelected.delete(itemId);
} else {
newSelected.add(itemId);
}
}
onChange?.(Array.from(newSelected));
return newSelected;
});
}, [onChange]);
const selectAll = () => {
const allIds = items.map(i => i.id);
setSelected(new Set(allIds));
onChange?.(allIds);
};
const clearAll = () => {
setSelected(new Set());
onChange?.([]);
};
const handleKeyDown = (e, index) => {
switch (e.key) {
case 'ArrowDown':
e.preventDefault();
setActiveIndex(Math.min(index + 1, items.length - 1));
break;
case 'ArrowUp':
e.preventDefault();
setActiveIndex(Math.max(index - 1, 0));
break;
case ' ':
e.preventDefault();
toggleSelection(items[index].id, e.shiftKey);
break;
case 'a':
if (e.ctrlKey || e.metaKey) {
e.preventDefault();
selectAll();
}
break;
}
};
return (
<div className="multi-select">
<div className="controls">
<button onClick={selectAll}>Select All</button>
<button onClick={clearAll}>Clear All</button>
<span>{selected.size} of {items.length} selected</span>
</div>
<label id="listbox-label">{label}</label>
<ul
role="listbox"
aria-labelledby="listbox-label"
aria-multiselectable="true"
tabIndex={0}
>
{items.map((item, index) => (
<li
key={item.id}
role="option"
aria-selected={selected.has(item.id)}
tabIndex={index === activeIndex ? 0 : -1}
onClick={(e) => toggleSelection(item.id, e.shiftKey)}
onKeyDown={(e) => handleKeyDown(e, index)}
className={selected.has(item.id) ? 'selected' : ''}
>
<span className="checkbox">
{selected.has(item.id) ? 'β' : 'β'}
</span>
{item.label}
</li>
))}
</ul>
{/* Announce selection changes */}
<div role="status" aria-live="polite" className="sr-only">
{selected.size} items selected
</div>
</div>
);
}
// Usage
function App() {
const languages = [
{ id: 'js', label: 'JavaScript' },
{ id: 'ts', label: 'TypeScript' },
{ id: 'py', label: 'Python' },
{ id: 'go', label: 'Go' },
{ id: 'rust', label: 'Rust' },
];
return (
<MultiSelectListbox
items={languages}
label="Select your favorite languages"
initialSelected={['js', 'ts']}
onChange={(selected) => console.log('Selected:', selected)}
/>
);
}Best Practices
Set aria-multiselectable on the container (listbox, grid, tree), not on items
Use aria-selected on each item to indicate its selection state
Support keyboard shortcuts: Ctrl+Click, Shift+Click, Ctrl+A
Provide "Select All" and "Clear" actions for convenience
Announce selection count changes via aria-live regions
Visually indicate multi-select capability (checkboxes, selection count)
Don't put aria-multiselectable on individual itemsβit goes on the container
Don't use on tablists unless tabs can actually be multi-selected (rare)
Don't forget to update aria-selected when selection changes

