Rooks
HooksState Management

useMultiSelectableList

Manages multiple-selection list state with toggle and select-all helpers.

About

A custom hook to easily select multiple values from a list

Examples

import { useMultiSelectableList } from "rooks";

const toppings = ["Mushroom", "Olive", "Pepper"];

export default function ToppingPicker() {
  const [[, selected], { matchSelection, toggleSelection }] =
    useMultiSelectableList(toppings, [0], true);

  return (
    <fieldset>
      <legend>Toppings: {selected?.join(", ") || "none"}</legend>
      {toppings.map((topping) => (
        <label key={topping}>
          <input
            type="checkbox"
            checked={matchSelection({ value: topping })}
            onChange={toggleSelection({ value: topping })}
          />
          {topping}
        </label>
      ))}
    </fieldset>
  );
}
import { useEffect, useState } from "react";
import { useMultiSelectableList } from "rooks";
import { createGlobalStyle } from "styled-components";

const GlobalStyles = createGlobalStyle`
  .App {
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen,
    Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif;
}

h3 {
  text-align: center;
}

.topping {
  margin-top: 0.3rem;
  vertical-align: text-bottom;
}

.result {
  margin-top: 1rem;
}

.toppings-list,
.total {
  width: 30%;
  margin: 0 auto;
}

.toppings-list {
  list-style: none;
  padding: 0;
}

.toppings-list li {
  margin-bottom: 0.5rem;
}

.toppings-list-item {
  display: flex;
  justify-content: space-between;
}

.toppings-list li:last-child {
  border-top: 1px solid #ccc;
  margin-top: 1rem;
  padding-top: 1rem;
}

.toppings-list-item label {
  vertical-align: text-bottom;
  margin-left: 0.2rem;
}

.total {
  margin-top: 1rem;
}

@media screen and (max-width: 600px) {
  .toppings-list,
  .total {
    width: 90%;
  }
}

`;

export const toppings = [
  {
    name: "Capsicum",
    price: 1.2,
  },
  {
    name: "Paneer",
    price: 2.0,
  },
  {
    name: "Red Paprika",
    price: 2.5,
  },
  {
    name: "Onions",
    price: 3.0,
  },
  {
    name: "Extra Cheese",
    price: 3.5,
  },
];

export default function App() {
  const [total, setTotal] = useState(0);
  const [selection, { matchSelection, toggleSelection, updateSelections }] =
    useMultiSelectableList(toppings, [0, 1]);

  useEffect(() => {
    setTotal(selection[1].reduce((acc, cur) => acc + cur.price, 0));
  }, [selection]);

  return (
    <div className="App">
      <GlobalStyles />
      <h3>useMultiSelectableList Example</h3>
      <ul className="toppings-list">
        {toppings.map(({ name, price }, index) => {
          return (
            <li key={index}>
              <div className="toppings-list-item">
                <div className="left-section">
                  <input
                    type="checkbox"
                    id={`custom-checkbox-${index}`}
                    name={name}
                    checked={matchSelection({ index })}
                    onChange={() => toggleSelection({ index })()}
                  />
                  <label htmlFor={`custom-checkbox-${index}`}>{name}</label>
                </div>
                <div className="right-section">{price}</div>
              </div>
            </li>
          );
        })}
        <li>
          <div className="toppings-list-item">
            <div className="left-section">Total:</div>
            <div className="right-section">{total}</div>
          </div>
        </li>
      </ul>
    </div>
  );
}

Parameters

Argument valueTypeDescriptionDefault value
listArrayA list of items of any type[]
initialSelectIndicesArray&lt;number&gt;An array of indices that are selected initially[0]
allowUnselectedBooleanWhether to allow unselect when update selectionsfalse

Return value

Returns an array of following items:

Return valueTypeDescription
selectionArrayThe first item is an array of selected indices, the second item is the selected values
methodsObjectObject with methods to control the selectable list, see the table below

Methods:

MethodsTypeDescription
matchSelection({ index?: number, value?: T }) => booleanreturns true if the item is selected
toggleSelection({ index?: number, value?: T }) => () => voidreturns a function to toggle an item by index or value
updateSelections({ indices?: number[], values?: T[] }) => () => voidreturns a function to update specified items

Behavior and lifecycle

Selection is stored as indices; selected values are derived from the current list on every render, and missing indices are filtered out. Control factories return event-handler functions rather than updating immediately. Passing both index and value, neither field, an empty selection while allowUnselected is false, or values absent from the list produces a console warning and may leave state unchanged. Matching uses Array.includes reference equality.

Compatibility and accessibility

The hook uses only React state and is SSR-safe. For multi-select UI, use native checkboxes or expose equivalent aria-checked/aria-selected state, keep every control keyboard accessible, and label the group.

On this page