use-selection
Generic single- or multi-select state for lists, tables, and grids.
| Package | @vizejs/composable/use-selection |
| Own the source | vize lib pull composable:use-selection |
| Runtime exports | useSelection |
| Gzip budget | 1280 B |
Minimal setup
Use a Vue 3.5+ project with Vue SFC compilation configured (see Vite+ integration).
vp install @vizejs/composable
Import the entry directly. Call lifecycle-bound utilities inside <script setup> so cleanup follows the component scope; consult the runtime contract for this entry's SSR and hydration behavior.
import { useSelection } from "@vizejs/composable/use-selection";
Try it
Choose workshop sessions with stable identities
Select up to two available sessions with native checkboxes, keeping the selected items accurate when catalogue objects change.
What to observe: Use Space on a checkbox, then choose a second session. The full workshop and further choices are disabled. Clear session choices restores an empty selection. Select available sessions fills only the two-place limit. Revise compiler title replaces all catalogue objects while keeping the selected stable ids and showing the updated title.
multiple: true makes selected an array; getKey gives each item a stable id. The composable's max and isSelectable constrain select/selectAll, and the example mirrors those constraints in native disabled states. selected follows catalogue order, while selectedKeys preserves selection order. clear() removes choices without changing catalogue data. Title revision is an explicit local immutable update, not a simulated server response. This synchronous selection state needs no browser globals or cleanup and renders the same empty state during SSR.
Compare the initial and interacted states


Copy the example
Save this complete SFC in your Vue project. Its public imports and source are the same ones executed in the preview.
<script setup lang="ts">
import { ref, useId } from "vue";
import { useSelection } from "@vizejs/composable/use-selection";
const selectionId = useId();
const revised = ref(false);
const sessions = ref([
{ id: "compiler", title: "Compiler clinic", full: false },
{ id: "types", title: "Type checking clinic", full: false },
{ id: "ssr", title: "SSR workshop (full)", full: true },
{ id: "editor", title: "Editor tools clinic", full: false },
]);
const { selected, count, isSelected, toggle, selectAll, clear } = useSelection({
items: sessions,
multiple: true,
max: 2,
getKey: (item) => item.id,
isSelectable: (item) => !item.full,
});
function reviseCompilerTitle(): void {
sessions.value = sessions.value.map((item) => ({
...item,
title: item.id === "compiler" ? "Compiler clinic (updated)" : item.title,
}));
revised.value = true;
}
</script>
<template>
<div class="composable-example">
<p>
Choose up to two sessions from this local catalogue. The full workshop cannot be selected.
</p>
<fieldset>
<legend>Workshop sessions</legend>
<div v-for="item in sessions" :key="item.id">
<input
:id="`${selectionId}-${item.id}`"
type="checkbox"
class="session-checkbox"
:checked="isSelected(item)"
:disabled="item.full || (count >= 2 && !isSelected(item))"
@change="() => toggle(item)"
/>
<label :for="`${selectionId}-${item.id}`" class="session-label">{{ item.title }}</label>
</div>
</fieldset>
<output aria-live="polite">{{ count }} of 2 places used</output>
<p v-if="count === 2">Your two-session limit is reached. Clear a choice to select another.</p>
<p v-if="count === 0">No sessions selected.</p>
<ul v-else aria-label="Selected sessions">
<li v-for="item in selected" :key="item.id">{{ item.title }}</li>
</ul>
<div class="example-actions">
<button type="button" @click="selectAll">Select available sessions</button>
<button type="button" :disabled="count === 0" @click="clear">Clear session choices</button>
<button type="button" :disabled="revised" @click="reviseCompilerTitle">
Revise compiler title
</button>
</div>
<p>
Revising the title replaces the catalogue's objects. Selected sessions keep their stable ids.
</p>
</div>
</template>
<style scoped>
.session-checkbox {
inline-size: auto;
}
.session-label {
display: inline;
}
</style>
Runtime contract
| Utility | Category | Stability | SSR | Hydration | Cleanup | Targets | Host globals | Uses |
|---|---|---|---|---|---|---|---|---|
useSelection |
state | experimental | safe | stable | none | web, server, worker, native, desktop, terminal | — | — |
API
useSelection
Generic single- or multi-select state for lists, tables, and grids. Keys (from getKey, the item itself by default) identify items, so the selection survives items being replaced. multiple: true switches the selected type to an array; max, required, and isSelectable constrain changes, and extendTo implements shift-click ranges from the anchor in display order. Synchronous state: SSR-safe and nothing to dispose.
function useSelection<Item, Key = Item, const Multiple extends boolean = false>( options: UseSelectionOptions<Item, Key, Multiple>, ): Selection<Item, Key, Multiple>
const rows = useSelection({ items: users, multiple: true, getKey: (user) => user.id });
rows.toggle(user);
rows.extendTo(other); // shift-click
useSelection
function useSelection( options: UseSelectionOptions<unknown, unknown, boolean>, ): Selection<unknown, unknown, boolean>
Types
UseSelectionOptions
Options for useSelection.
| Member | Type | Description |
|---|---|---|
items |
MaybeRefOrGetter<readonly Item[]> |
Selectable items, in display order. Reactive. |
multiple? |
Multiple |
Allow several selected items. |
getKey? |
(item: Item) => Key |
Stable identity of an item, for example its id. Keys survive item objects being replaced (refetches, immutable updates). |
initial? |
readonly Key[] |
Keys selected initially. |
max? |
number |
Maximum number of selected items in multiple mode; selecting beyond it is refused. |
required? |
boolean |
Refuse to deselect the last selected item. |
isSelectable? |
(item: Item) => boolean |
Veto selecting an item (for example disabled rows). |
Selection
Selection state and commands returned by useSelection.
| Member | Type | Description |
|---|---|---|
selectedKeys |
Readonly<ShallowRef<readonly Key[]>> |
Selected keys in selection order. |
selected |
ComputedRef<Multiple extends true ? Item[] : Item | undefined> |
Selected items in items order: an array in multiple mode, the single item (or undefined) otherwise. Keys whose item is currently absent are kept but not listed. |
count |
ComputedRef<number> |
Number of selected keys. |
isAllSelected |
ComputedRef<boolean> |
Whether every selectable item is selected (multiple mode). |
isIndeterminate |
ComputedRef<boolean> |
Whether some but not all selectable items are selected (tri-state checkboxes). |
anchor |
Readonly<ShallowRef<Key | undefined>> |
Item that anchors range selection (the last explicitly selected one). |
isSelected |
(item: Item) => boolean |
Whether item is selected. |
select |
(item: Item) => boolean |
Select item (replacing the selection in single mode). |
deselect |
(item: Item) => boolean |
Deselect item. |
toggle |
(item: Item) => boolean |
Toggle item. |
selectOnly |
(item: Item) => boolean |
Select exactly item. |
extendTo |
(item: Item) => boolean |
Select every item between the anchor and item inclusive (shift-click); in single mode this selects item. |
selectAll |
() => void |
Select every selectable item, up to max (multiple mode). |
clear |
() => void |
Clear the selection (ignores required). |