List Selection
Used for managing selection state in list collections.
The useListSelection
hook manages selection state in lists and collections. It supports single and multiple selection
modes with operations like select, deselect, toggle, and clear.
import { createListCollection, useListSelection } from '@ark-ui/react/collection'
const collection = createListCollection({
items: [
{ label: 'Apple', value: 'apple' },
{ label: 'Banana', value: 'banana' },
{ label: 'Cherry', value: 'cherry' },
],
})
const selection = useListSelection({
collection,
selectionMode: 'single',
deselectable: true,
})
console.log(selection.selectedValues) // ['apple', 'banana', 'cherry']
Examples
Basic
By default, the hook supports single selection mode that can be deselected.
Set
deselectable
tofalse
to prevent deselecting the current selection.
import { createListCollection, useListSelection } from '@ark-ui/react/collection'
export const ListSelection = () => {
const collection = createListCollection({
items: ['React', 'Vue', 'Angular'],
})
const selection = useListSelection({
collection,
})
return (
<div>
<pre>{JSON.stringify(selection.selectedValues)}</pre>
{collection.items.map((item) => (
<label
key={item}
style={{
display: 'flex',
alignItems: 'center',
gap: 8,
userSelect: 'none',
backgroundColor: selection.isSelected(item) ? 'lightblue' : 'white',
}}
>
<input type="checkbox" checked={selection.isSelected(item)} onChange={() => selection.select(item)} />
<span>{item}</span>
</label>
))}
</div>
)
}
import { createListCollection, useListSelection } from '@ark-ui/solid/collection'
import { For } from 'solid-js'
export const ListSelection = () => {
const collection = createListCollection({
items: ['React', 'Vue', 'Angular'],
})
const selection = useListSelection({
collection,
})
return (
<div>
<pre>{JSON.stringify(selection.selectedValues())}</pre>
<For each={collection.items}>
{(item) => (
<label
style={{
display: 'flex',
'align-items': 'center',
gap: '8px',
'user-select': 'none',
'background-color': selection.isSelected(item) ? 'lightblue' : 'white',
}}
>
<input type="checkbox" checked={selection.isSelected(item)} onChange={() => selection.select(item)} />
<span>{item}</span>
</label>
)}
</For>
</div>
)
}
<script setup lang="ts">
import { createListCollection, useListSelection } from '@ark-ui/vue/collection'
const collection = createListCollection({
items: ['React', 'Vue', 'Angular'],
})
const selection = useListSelection({
collection,
})
</script>
<template>
<div>
<pre>{{ JSON.stringify(selection.selectedValues.value) }}</pre>
<label
v-for="item in collection.items"
:key="item"
:style="{
display: 'flex',
alignItems: 'center',
gap: '8px',
userSelect: 'none',
backgroundColor: selection.isSelected(item) ? 'lightblue' : 'white',
}"
>
<input type="checkbox" :checked="selection.isSelected(item)" @change="selection.select(item)" />
<span>{{ item }}</span>
</label>
</div>
</template>
<script lang="ts">
import { useListSelection, createListCollection } from '@ark-ui/svelte/collection'
const collection = createListCollection({
items: ['React', 'Vue', 'Angular'],
})
const selection = useListSelection({
collection,
})
</script>
<div>
<pre>{JSON.stringify(selection.selectedValues())}</pre>
{#each collection.items as item (item)}
<label
style:display="flex"
style:align-items="center"
style:gap="8px"
style:user-select="none"
style:background-color={selection.isSelected(item) ? 'lightblue' : 'white'}
>
<input type="checkbox" checked={selection.isSelected(item)} onchange={() => selection.select(item)} />
<span>{item}</span>
</label>
{/each}
</div>
Multiple Selection
Set selectionMode
to multiple
to allow multiple items to be selected.
import { createListCollection, useListSelection } from '@ark-ui/react/collection'
export const ListSelectionMultiple = () => {
const collection = createListCollection({
items: ['React', 'Vue', 'Angular', 'Svelte', 'Solid'],
})
const selection = useListSelection({
collection,
selectionMode: 'multiple',
})
const handleSelectAll = () => {
if (selection.isAllSelected()) {
selection.clear()
} else {
selection.setSelectedValues(collection.getValues())
}
}
return (
<div>
<div style={{ marginBottom: 16, display: 'flex', alignItems: 'center', gap: 16 }}>
<button onClick={handleSelectAll}>{selection.isAllSelected() ? 'Deselect All' : 'Select All'}</button>
<span>
{selection.selectedValues.length} of {collection.items.length} selected
</span>
</div>
{collection.items.map((item) => (
<label
key={item}
style={{
display: 'flex',
alignItems: 'center',
gap: 8,
userSelect: 'none',
backgroundColor: selection.isSelected(item) ? 'lightblue' : 'white',
}}
>
<input type="checkbox" checked={selection.isSelected(item)} onChange={() => selection.select(item)} />
<span>{item}</span>
</label>
))}
</div>
)
}
import { createListCollection, useListSelection } from '@ark-ui/solid/collection'
import { For } from 'solid-js'
export const ListSelectionMultiple = () => {
const collection = createListCollection({
items: ['React', 'Vue', 'Angular', 'Svelte', 'Solid'],
})
const selection = useListSelection({
collection,
selectionMode: 'multiple',
})
const handleSelectAll = () => {
if (selection.isAllSelected()) {
selection.clear()
} else {
selection.setSelectedValues(collection.getValues())
}
}
return (
<div>
<div style={{ 'margin-bottom': '16px', display: 'flex', 'align-items': 'center', gap: '16px' }}>
<button onClick={handleSelectAll}>{selection.isAllSelected() ? 'Deselect All' : 'Select All'}</button>
<span>
{selection.selectedValues().length} of {collection.items.length} selected
</span>
</div>
<For each={collection.items}>
{(item) => (
<label
style={{
display: 'flex',
'align-items': 'center',
gap: '8px',
'user-select': 'none',
'background-color': selection.isSelected(item) ? 'lightblue' : 'white',
}}
>
<input type="checkbox" checked={selection.isSelected(item)} onChange={() => selection.select(item)} />
<span>{item}</span>
</label>
)}
</For>
</div>
)
}
<script setup lang="ts">
import { createListCollection, useListSelection } from '@ark-ui/vue/collection'
const collection = createListCollection({
items: ['React', 'Vue', 'Angular', 'Svelte', 'Solid'],
})
const selection = useListSelection({
collection,
selectionMode: 'multiple',
})
const handleSelectAll = () => {
if (selection.isAllSelected()) {
selection.clear()
} else {
selection.setSelectedValues(collection.getValues())
}
}
</script>
<template>
<div>
<div :style="{ marginBottom: '16px', display: 'flex', alignItems: 'center', gap: '16px' }">
<button @click="handleSelectAll">
{{ selection.isAllSelected() ? 'Deselect All' : 'Select All' }}
</button>
<span>{{ selection.selectedValues.value.length }} of {{ collection.items.length }} selected</span>
</div>
<label
v-for="item in collection.items"
:key="item"
:style="{
display: 'flex',
alignItems: 'center',
gap: '8px',
userSelect: 'none',
backgroundColor: selection.isSelected(item) ? 'lightblue' : 'white',
}"
>
<input type="checkbox" :checked="selection.isSelected(item)" @change="selection.select(item)" />
<span>{{ item }}</span>
</label>
</div>
</template>
<script lang="ts">
import { createListCollection, useListSelection } from '@ark-ui/svelte/collection'
const collection = createListCollection({
items: ['React', 'Vue', 'Angular', 'Svelte', 'Solid'],
})
const selection = useListSelection({
collection,
selectionMode: 'multiple',
})
const handleSelectAll = () => {
if (selection.isAllSelected()) {
selection.clear()
} else {
selection.setSelectedValues(collection.getValues())
}
}
</script>
<div>
<div style="margin-bottom: 16px; display: flex; align-items: center; gap: 16px;">
<button onclick={handleSelectAll}>
{selection.isAllSelected() ? 'Deselect All' : 'Select All'}
</button>
<span>
{selection.selectedValues().length} of {collection.items.length} selected
</span>
</div>
{#each collection.items as item (item)}
<label
style="display: flex; align-items: center; gap: 8px; user-select: none;"
style:background-color={selection.isSelected(item) ? 'lightblue' : 'white'}
>
<input type="checkbox" checked={selection.isSelected(item)} onchange={() => selection.select(item)} />
<span>{item}</span>
</label>
{/each}
</div>
Range Selection
Here's an example of how to implement range selection that extends the selection from the first selected item to the clicked item.
import { createListCollection, useListSelection } from '@ark-ui/react/collection'
export const ListSelectionRange = () => {
const collection = createListCollection({
items: [
{ value: 'react', label: 'React' },
{ value: 'vue', label: 'Vue' },
{ value: 'angular', label: 'Angular' },
{ value: 'svelte', label: 'Svelte' },
{ value: 'solid', label: 'Solid' },
{ value: 'preact', label: 'Preact' },
{ value: 'qwik', label: 'Qwik' },
{ value: 'lit', label: 'Lit' },
],
})
const selection = useListSelection({
collection,
selectionMode: 'multiple',
})
const handleItemClick = (value: string, event: React.MouseEvent) => {
if (event.shiftKey && selection.firstSelectedValue) {
// Extend selection from first selected to clicked item
selection.extend(selection.firstSelectedValue, value)
} else if (event.ctrlKey || event.metaKey) {
// Toggle individual item
selection.toggle(value)
} else {
// Replace selection with clicked item
selection.replace(value)
}
}
return (
<div>
<div style={{ marginBottom: 16 }}>
<p>
<strong>Instructions:</strong>
</p>
<ul style={{ margin: '8px 0', paddingLeft: 20 }}>
<li>Click to select single item</li>
<li>Ctrl/Cmd + Click to toggle individual items</li>
<li>Shift + Click to select range from first selected item</li>
</ul>
</div>
{collection.items.map((item) => (
<label
key={item.value}
style={{
backgroundColor: selection.isSelected(item.value) ? '#e2e8f0' : 'transparent',
padding: '8px 12px',
cursor: 'pointer',
userSelect: 'none',
border: '1px solid #e2e8f0',
marginBottom: 2,
}}
>
<input
type="checkbox"
checked={selection.isSelected(item.value)}
onClick={(e) => handleItemClick(item.value, e)}
/>
{item.label}
</label>
))}
</div>
)
}
import { createListCollection, useListSelection } from '@ark-ui/solid/collection'
import { For } from 'solid-js'
export const ListSelectionRange = () => {
const items = [
{ value: 'react', label: 'React' },
{ value: 'vue', label: 'Vue' },
{ value: 'angular', label: 'Angular' },
{ value: 'svelte', label: 'Svelte' },
{ value: 'solid', label: 'Solid' },
{ value: 'preact', label: 'Preact' },
{ value: 'qwik', label: 'Qwik' },
{ value: 'lit', label: 'Lit' },
]
const collection = createListCollection({ items })
const selection = useListSelection({
collection,
selectionMode: 'multiple',
})
const handleItemClick = (value: string, event: MouseEvent) => {
const firstSelectedValue = selection.firstSelectedValue()
if (event.shiftKey && firstSelectedValue) {
// Extend selection from first selected to clicked item
selection.extend(firstSelectedValue, value)
} else if (event.ctrlKey || event.metaKey) {
// Toggle individual item
selection.toggle(value)
} else {
// Replace selection with clicked item
selection.replace(value)
}
}
return (
<div>
<div style={{ 'margin-bottom': '16px' }}>
<p>
<strong>Instructions:</strong>
</p>
<ul style={{ margin: '8px 0', 'padding-left': '20px' }}>
<li>Click to select single item</li>
<li>Ctrl/Cmd + Click to toggle individual items</li>
<li>Shift + Click to select range from first selected item</li>
</ul>
</div>
<For each={collection.items}>
{(item) => (
<label
style={{
'background-color': selection.isSelected(item.value) ? '#e2e8f0' : 'transparent',
padding: '8px 12px',
cursor: 'pointer',
'user-select': 'none',
border: '1px solid #e2e8f0',
'margin-bottom': '2px',
}}
>
<input
type="checkbox"
checked={selection.isSelected(item.value)}
onClick={(e) => handleItemClick(item.value, e)}
/>
{item.label}
</label>
)}
</For>
</div>
)
}
<script setup lang="ts">
import { createListCollection, useListSelection } from '@ark-ui/vue/collection'
const items = [
{ value: 'react', label: 'React' },
{ value: 'vue', label: 'Vue' },
{ value: 'angular', label: 'Angular' },
{ value: 'svelte', label: 'Svelte' },
{ value: 'solid', label: 'Solid' },
{ value: 'preact', label: 'Preact' },
{ value: 'qwik', label: 'Qwik' },
{ value: 'lit', label: 'Lit' },
]
const collection = createListCollection({ items })
const selection = useListSelection({
collection,
selectionMode: 'multiple',
})
const handleItemClick = (value: string, event: MouseEvent) => {
if (event.shiftKey && selection.firstSelectedValue.value) {
// Extend selection from first selected to clicked item
selection.extend(selection.firstSelectedValue.value, value)
} else if (event.ctrlKey || event.metaKey) {
// Toggle individual item
selection.toggle(value)
} else {
// Replace selection with clicked item
selection.replace(value)
}
}
</script>
<template>
<div>
<div style="margin-bottom: 16px">
<p><strong>Instructions:</strong></p>
<ul style="margin: 8px 0; padding-left: 20px">
<li>Click to select single item</li>
<li>Ctrl/Cmd + Click to toggle individual items</li>
<li>Shift + Click to select range from first selected item</li>
</ul>
</div>
<div
v-for="item in collection.items"
:key="item.value"
@click="(e) => handleItemClick(item.value, e)"
:style="{
backgroundColor: selection.isSelected(item.value) ? '#e2e8f0' : 'transparent',
padding: '8px 12px',
cursor: 'pointer',
userSelect: 'none',
border: '1px solid #e2e8f0',
marginBottom: '2px',
}"
>
{{ item.label }}
</div>
</div>
</template>
<script lang="ts">
import { createListCollection, useListSelection } from '@ark-ui/svelte/collection'
const items = [
{ value: 'react', label: 'React' },
{ value: 'vue', label: 'Vue' },
{ value: 'angular', label: 'Angular' },
{ value: 'svelte', label: 'Svelte' },
{ value: 'solid', label: 'Solid' },
{ value: 'preact', label: 'Preact' },
{ value: 'qwik', label: 'Qwik' },
{ value: 'lit', label: 'Lit' },
]
const collection = createListCollection({ items })
const selection = useListSelection({
collection,
selectionMode: 'multiple',
})
const handleItemClick = (value: string, event: MouseEvent) => {
const firstSelectedValue = selection.firstSelectedValue()
if (event.shiftKey && firstSelectedValue) {
// Extend selection from first selected to clicked item
selection.extend(firstSelectedValue, value)
} else if (event.ctrlKey || event.metaKey) {
// Toggle individual item
selection.toggle(value)
} else {
// Replace selection with clicked item
selection.replace(value)
}
}
</script>
<div>
<div style="margin-bottom: 16px;">
<p><strong>Instructions:</strong></p>
<ul style="margin: 8px 0; padding-left: 20px;">
<li>Click to select single item</li>
<li>Ctrl/Cmd + Click to toggle individual items</li>
<li>Shift + Click to select range from first selected item</li>
</ul>
</div>
{#each collection.items as item (item.value)}
<label
style="padding: 8px 12px; cursor: pointer; user-select: none; border: 1px solid #e2e8f0; margin-bottom: 2px;"
style:background-color={selection.isSelected(item.value) ? '#e2e8f0' : 'transparent'}
>
<input
type="checkbox"
checked={selection.isSelected(item.value)}
onclick={(e) => handleItemClick(item.value, e)}
/>
{item.label}
</label>
{/each}
</div>
API Reference
Props
- collection (
ListCollection<T>
) - The collection to manage selection for - selectionMode (
'single' | 'multiple' | 'none'
, default:'single'
) - The selection mode - deselectable (
boolean
, default:true
) - Whether selected items can be deselected - initialSelectedValues (
string[]
, default:[]
) - Initial selected values - resetOnCollectionChange (
boolean
, default:false
) - Whether to reset selection when collection changes
Return Value
The hook returns an object with the following properties and methods:
State Properties
- selectedValues (
string[]
) - Array of currently selected values - isEmpty (
boolean
) - Whether no items are selected - firstSelectedValue (
string | null
) - The first selected value in collection order - lastSelectedValue (
string | null
) - The last selected value in collection order
Query Methods
- isSelected (
(value: string | null) => boolean
) - Check if a value is selected - canSelect (
(value: string) => boolean
) - Check if a value can be selected - isAllSelected (
() => boolean
) - Check if all items are selected - isSomeSelected (
() => boolean
) - Check if some items are selected
Selection Methods
- select (
(value: string, forceToggle?: boolean) => void
) - Select a value - deselect (
(value: string) => void
) - Deselect a value - toggle (
(value: string) => void
) - Toggle selection of a value - replace (
(value: string | null) => void
) - Replace selection with a single value - extend (
(anchorValue: string, targetValue: string) => void
) - Extend selection from anchor to target - setSelectedValues (
(values: string[]) => void
) - Set the selected values - setSelection (
(values: string[]) => void
) - Set the selection (alias for setSelectedValues) - clear (
() => void
) - Clear all selections - resetSelection (
() => void
) - Reset selection to initial state