Multi Select

Production-ready multi select with extensive token integration, state management, and semantic HTML.

Overview

The Multi Select component is a core building block of the Byteion design system, providing a robust, accessibility-compliant interface for its specific UI capabilities.

Why this exists

Selecting multiple values from a list is a common pattern for filters, tags, and assignments. A multi-select shows selected values as removable pills.

Mental model

A multi-select is a combobox that accumulates selections. Each selection becomes a removable tag pill inside the input.

When to use

• Tag or category assignment • Multi-value filters in data tables • Assigning multiple users to a task • Skill or interest selection

When not to use

• Do not use for single selection — use select or combobox • Avoid for mutually exclusive options — use checkboxes • Do not use when there are only 2-3 options — use checkboxes instead

Basic example

The following example demonstrates standard usage, fully compliant with Byteion's token-first approach.

Variants

Each variant communicates a different meaning. Choose based on priority, not aesthetics.

Single default variant.

Sizes

Size should match the importance and context of the component.

  • md — md size.

States

This component reacts seamlessly to the following interaction and data states:

  • default — Resting appearance before interaction.
  • hover — Pointer is over the element — visual feedback via color or shadow.
  • focus — Keyboard or tab focus — shows ring indicator for accessibility.
  • active — Currently selected or in use.
  • disabled — Non-interactive — grayed out, cursor not-allowed.
  • loading — Action in progress — spinner replaces icon, button is disabled.
  • invalid — Input value does not match expected format.
  • error — Validation failed — red border and error message.
  • success — Validation passed — green indicator.

Anatomy

• Input area — contains selected tag pills • Tag pill — removable chip with label and X • Dropdown — filtered remaining options • Input cursor — type to filter

Usage guidelines

  • Show selected items as removable pills.
  • Filter out already-selected items from the dropdown.
  • Set a reasonable max selection limit.

Do and Don't

Do

  • Show tags as removable pills.
  • Filter out selected items.
  • Set a max limit if needed.

Don't

  • Do not allow duplicate selections.
  • Do not hide what is already selected.
  • Do not use for single-value fields.

Accessibility

Each tag pill is focusable and removable via Backspace or click.

Role:listbox combobox

ARIA Attributes

  • aria-expanded
  • aria-multiselectable
  • aria-selected
  • aria-activedescendant

Keyboard Navigation

  • ArrowDown: Moves focus to next option
  • ArrowUp: Moves focus to previous option
  • Enter: Adds or removes the highlighted option
  • Backspace: Removes the last selected item when input is empty
  • Escape: Closes the listbox

Customization

Tag pills use badge tokens.

Compose with React cva()

Use the exportedcva() +cn() utilities to compose your own variant on top of the Byteion tokens.

import { cva, cn, type VariantProps } from '@byteion/react';

const myMultiSelect = cva('base classes…', {
 variants: {
  accent: { soft:'border-primary-200', loud:'border-primary-500' },
 },
 defaultVariants: { accent:'soft' },
});

type Props = React.HTMLAttributes<HTMLDivElement> & VariantProps<typeof myMultiSelect>;

Compose with Vue cva()

Use the exportedcva() +cn() utilities inside standard Vue setup blocks.

<script setup lang="ts">
import { computed, useAttrs } from 'vue';
import { cva, type VariantProps } from '@byteion/vue';

interface Props { accent?:'soft' |'loud' }
const props = withDefaults(defineProps<Props>(), { accent:'soft' });
const attrs = useAttrs();

const myMultiSelect = cva('base classes…', {
  variants: { accent: { soft:'border-primary-200', loud:'border-primary-500' } },
});

const cls = computed(() => myMultiSelect({ accent: props.accent, class: attrs.class as string | undefined }));
</script>

Runtime events

Byteion keeps framework APIs idiomatic: HTML uses DOM custom events, React uses callback props, and Vue uses emitted events / v-model updates.

HTML / JS events

  • bi:ms:add
  • bi:ms:change
  • bi:ms:close
  • bi:ms:open
  • bi:ms:remove
  • bi:multi-select:add
  • bi:multi-select:change
  • bi:multi-select:error
  • bi:multi-select:load
  • bi:multi-select:remove
  • bi:multi-select:search

React callbacks

  • onValueChange(value: string[]) => void

Vue emits

  • update:modelValue[v: string[]]

Remote / AJAX

Remote options

Add data-bi-source when option choices should be loaded as the user types. Selected values are kept in hidden inputs, so forms still submit progressively once options are chosen.

Attributes

  • data-bi-source
  • data-bi-search-param
  • data-bi-min-chars
  • data-bi-debounce
  • data-bi-value-field
  • data-bi-label-field
  • data-bi-name
  • data-bi-loading-text
  • data-bi-empty-text
  • data-bi-error-text

Runtime events

  • bi:multi-select:search
  • bi:multi-select:load
  • bi:multi-select:error
  • bi:multi-select:add
  • bi:multi-select:remove
  • bi:multi-select:change
Backend response: Remote endpoints must return JSON. Byteion accepts an array or an object with data, items, results, or options. Each item can be a string/number or an object; value resolves from value, id, or key, and label resolves from label, name, text, or the value. Override object fields with data-bi-value-field and data-bi-label-field.

Accessibility: loading sets aria-busy="true"; empty states use role="status"; error states use alert/status messaging where the runtime renders remote status rows.

API Reference

Requires JS module for tag management.

HTML Data Attributes

No custom data attributes required.

React Props · MultiSelectProps

Extracted directly fromreact/src/components/MultiSelect.tsx.

export interface MultiSelectProps {
  options: MultiSelectOption[];
  /** Controlled list of selected values. */
  value?: string[];
  defaultValue?: string[];
  onValueChange?: (value: string[]) => void;
  /** Visible label rendered above the field. */
  label?: ReactNode;
  helperText?: ReactNode;
  error?: ReactNode;
  /** Placeholder shown when nothing is selected. */
  placeholder?: string;
  /** Maximum number of selections. */
  max?: number;
  disabled?: boolean;
  /** Optional id for the input element. */
  id?: string;
  className?: string;
}

Vue Props · MultiSelectProps

Extracted directly fromvue/src/components/MultiSelect.vue.

export interface MultiSelectProps {
  modelValue?: string[];
  defaultValue?: string[];
  options: MultiSelectOption[];
  placeholder?: string;
  label?: string;
  helperText?: string;
  error?: string;
  disabled?: boolean;
  max?: number;
  id?: string;
}

Developer tips

• Best practice: when many items are selected, collapse them into"+3 more".

Common mistakes

Avoid hardcoded neutral colors, placeholder-only labels, duplicated primary actions, and missing keyboard states. Keep this component on Byteion tokens so dashboard, admin, POS, ERP, ecommerce back-office, Laravel, React, and Vue starter-kit screens can be rebranded without rewriting markup.

Production usage tips

Use the canonical HTML as the source of truth, keep React and Vue wrappers aligned with the documented variants, test dark mode and branded themes before release, and wire interactive behavior through data-bi-* attributes instead of styling hooks.

Premium examples

Advanced patterns available in Byteion Pro:

• Drag-to-reorder selected tags • Async search with server-side filtering

Unlock Premium Patterns

Get access to copy-paste ready Pro blocks, SaaS templates, and advanced data-tables built with this component.

Explore Pro Components