/Documentation

Chips

PublishedUpdated

Chips are compact elements that stand for one discrete thing. M3 has four types: assist chips for a smart or automated action, filter chips to narrow content, input chips for something the user entered, such as a recipient, and suggestion chips for a generated suggestion, such as a reply. See the M3 chips guidelines.

createChips builds a set, which owns the selection, removal and keyboard navigation of its chips; createAssistChip, createFilterChip, createInputChip and createSuggestionChip build one chip on its own.

Usage

A set is multi-select unless multiSelect is false. Its chips are configurations, which the set builds; without a type, a chip in a set is a filter chip.

Vanilla
import { createChips } from 'mtrl';

const chips = createChips({
  label: 'Categories',
  chips: [
    { type: 'filter', label: 'JavaScript', value: 'js' },
    { type: 'filter', label: 'TypeScript', value: 'ts' },
    { type: 'filter', label: 'CSS', value: 'css' },
  ],
});
document.body.append(chips.element);
Web Components
<m-chips label="Categories">
  <m-chip value="js" variant="filter">JavaScript</m-chip>
  <m-chip value="ts" variant="filter">TypeScript</m-chip>
  <m-chip value="css" variant="filter">CSS</m-chip>
</m-chips>
React
import { Chips, Chip } from 'mtrl/react';

export function Example() {
  return (
    <Chips label="Categories">
      <Chip value="js" variant="filter">JavaScript</Chip>
      <Chip value="ts" variant="filter">TypeScript</Chip>
      <Chip value="css" variant="filter">CSS</Chip>
    </Chips>
  );
}
Vue
<script setup lang="ts">
import { MChips, MChip } from 'mtrl/vue';
</script>

<template>
  <MChips label="Categories">
    <MChip value="js" variant="filter">JavaScript</MChip>
    <MChip value="ts" variant="filter">TypeScript</MChip>
    <MChip value="css" variant="filter">CSS</MChip>
  </MChips>
</template>
Svelte
<script lang="ts">
  import { Chips, Chip } from 'mtrl/svelte';
</script>

<Chips label="Categories">
  <Chip value="js" variant="filter">JavaScript</Chip>
  <Chip value="ts" variant="filter">TypeScript</Chip>
  <Chip value="css" variant="filter">CSS</Chip>
</Chips>
SolidJS
import { Chips, Chip } from 'mtrl/solid';

export function Example() {
  return (
    <Chips label="Categories">
      <Chip value="js" variant="filter">JavaScript</Chip>
      <Chip value="ts" variant="filter">TypeScript</Chip>
      <Chip value="css" variant="filter">CSS</Chip>
    </Chips>
  );
}

change reports the selection: the factory calls its handlers with the selected values and the value that changed, the web component's detail has value, an array in a multi-select set and a string or null in a single-select one.

Examples

Single selection

selectionRequired keeps the last selected chip selected, in either mode.

Vanilla
import { createChips } from 'mtrl';

const chips = createChips({
  label: 'Size',
  multiSelect: false,
  selectionRequired: true,
  chips: [
    { type: 'filter', label: 'S', value: 's' },
    { type: 'filter', label: 'M', value: 'm', selected: true },
    { type: 'filter', label: 'L', value: 'l' },
  ],
});
document.body.append(chips.element);
Web Components
<m-chips value="m" selection-required label="Size" selection="single">
  <m-chip value="s" variant="filter">S</m-chip>
  <m-chip value="m" variant="filter">M</m-chip>
  <m-chip value="l" variant="filter">L</m-chip>
</m-chips>
React
import { useState } from 'react';
import { Chips, Chip } from 'mtrl/react';

export function Example() {
  const [value, setValue] = useState("m");
  return (
    <Chips value={value} onChange={(event) => setValue(event.detail.value)} selectionRequired label="Size" selection="single">
      <Chip value="s" variant="filter">S</Chip>
      <Chip value="m" variant="filter">M</Chip>
      <Chip value="l" variant="filter">L</Chip>
    </Chips>
  );
}
Vue
<script setup lang="ts">
import { ref } from 'vue';
import { MChips, MChip } from 'mtrl/vue';

const value = ref("m");
</script>

<template>
  <MChips v-model="value" selection-required label="Size" selection="single">
    <MChip value="s" variant="filter">S</MChip>
    <MChip value="m" variant="filter">M</MChip>
    <MChip value="l" variant="filter">L</MChip>
  </MChips>
</template>
Svelte
<script lang="ts">
  import { Chips, Chip } from 'mtrl/svelte';

  let value = $state("m");
</script>

<Chips bind:value selectionRequired label="Size" selection="single">
  <Chip value="s" variant="filter">S</Chip>
  <Chip value="m" variant="filter">M</Chip>
  <Chip value="l" variant="filter">L</Chip>
</Chips>
SolidJS
import { createSignal } from 'solid-js';
import { Chips, Chip } from 'mtrl/solid';

export function Example() {
  const [value, setValue] = createSignal("m");
  return (
    <Chips value={value()} onChange={(event) => setValue(event.detail.value)} selectionRequired label="Size" selection="single">
      <Chip value="s" variant="filter">S</Chip>
      <Chip value="m" variant="filter">M</Chip>
      <Chip value="l" variant="filter">L</Chip>
    </Chips>
  );
}

Input chips

Every input chip has a remove button, and Backspace or Delete removes it when focused. In a set, the set removes it and emits remove with the chip; on its own, it leaves the page. avatar puts a 24dp round image in place of the leading icon.

Vanilla
import { createChips } from 'mtrl';

const chips = createChips({
  label: 'To',
  chips: [
    { type: 'input', label: 'Ada Lovelace', value: '[email protected]' },
    { type: 'input', label: 'Alan Turing', value: '[email protected]' },
  ],
});
document.body.append(chips.element);
Web Components
<m-chips label="To">
  <m-chip value="[email protected]" variant="input">Ada Lovelace</m-chip>
  <m-chip value="[email protected]" variant="input">Alan Turing</m-chip>
</m-chips>
React
import { Chips, Chip } from 'mtrl/react';

export function Example() {
  return (
    <Chips label="To">
      <Chip value="[email protected]" variant="input">Ada Lovelace</Chip>
      <Chip value="[email protected]" variant="input">Alan Turing</Chip>
    </Chips>
  );
}
Vue
<script setup lang="ts">
import { MChips, MChip } from 'mtrl/vue';
</script>

<template>
  <MChips label="To">
    <MChip value="[email protected]" variant="input">Ada Lovelace</MChip>
    <MChip value="[email protected]" variant="input">Alan Turing</MChip>
  </MChips>
</template>
Svelte
<script lang="ts">
  import { Chips, Chip } from 'mtrl/svelte';
</script>

<Chips label="To">
  <Chip value="[email protected]" variant="input">Ada Lovelace</Chip>
  <Chip value="[email protected]" variant="input">Alan Turing</Chip>
</Chips>
SolidJS
import { Chips, Chip } from 'mtrl/solid';

export function Example() {
  return (
    <Chips label="To">
      <Chip value="[email protected]" variant="input">Ada Lovelace</Chip>
      <Chip value="[email protected]" variant="input">Alan Turing</Chip>
    </Chips>
  );
}

Assist chips

elevated gives assist, filter and suggestion chips the elevated style in place of the outline.

Vanilla
import { createChips } from 'mtrl';

const chips = createChips({
  label: 'Actions',
  chips: [
    {
      type: 'assist',
      label: 'Add to calendar',
      value: 'calendar',
      leadingIcon: calendarIcon,
      elevated: true,
    },
    {
      type: 'assist',
      label: 'Directions',
      value: 'directions',
      leadingIcon: locationIcon,
      elevated: true,
    },
  ],
});
document.body.append(chips.element);
Web Components
<m-chips label="Actions">
  <m-chip value="calendar" variant="assist" elevated>Add to calendar</m-chip>
  <m-chip value="directions" variant="assist" elevated>Directions</m-chip>
</m-chips>

<script type="module">
  const chips = document.querySelector('m-chips');
  chips.querySelector('m-chip[value="calendar"]').setAttribute('icon', calendarIcon);
  chips.querySelector('m-chip[value="directions"]').setAttribute('icon', locationIcon);
</script>
React
import { Chips, Chip } from 'mtrl/react';
import { calendarIcon, locationIcon } from './app';

export function Example() {
  return (
    <Chips label="Actions">
      <Chip value="calendar" variant="assist" icon={calendarIcon} elevated>Add to calendar</Chip>
      <Chip value="directions" variant="assist" icon={locationIcon} elevated>Directions</Chip>
    </Chips>
  );
}
Vue
<script setup lang="ts">
import { MChips, MChip } from 'mtrl/vue';
import { calendarIcon, locationIcon } from './app';
</script>

<template>
  <MChips label="Actions">
    <MChip value="calendar" variant="assist" :icon="calendarIcon" elevated>Add to calendar</MChip>
    <MChip value="directions" variant="assist" :icon="locationIcon" elevated>Directions</MChip>
  </MChips>
</template>
Svelte
<script lang="ts">
  import { Chips, Chip } from 'mtrl/svelte';
  import { calendarIcon, locationIcon } from './app';
</script>

<Chips label="Actions">
  <Chip value="calendar" variant="assist" icon={calendarIcon} elevated>Add to calendar</Chip>
  <Chip value="directions" variant="assist" icon={locationIcon} elevated>Directions</Chip>
</Chips>
SolidJS
import { Chips, Chip } from 'mtrl/solid';
import { calendarIcon, locationIcon } from './app';

export function Example() {
  return (
    <Chips label="Actions">
      <Chip value="calendar" variant="assist" icon={calendarIcon} elevated>Add to calendar</Chip>
      <Chip value="directions" variant="assist" icon={locationIcon} elevated>Directions</Chip>
    </Chips>
  );
}

A filter chip's trailingMenu gives it a trailing button that opens a menu; onTrailingClick handles it. A filter chip opening a price menu is planned for Examples. A chip your app makes draggable shows M3's dragged state from dragstart to dragend; mtrl does not move chips itself.

API

Options

A chip

Passed to the four factories, and in a set's chips or addChip(), with type.

Option Type Default Description
type 'assist' | 'filter' | 'input' | 'suggestion' 'filter' In a set's chips or addChip(); the factories set it
label string '' The chip's label
value string derived from the label Identifies the chip to its set and to forms
leadingIcon string undefined Leading icon as SVG markup (icon is an alias)
trailingIcon string undefined Trailing icon as SVG markup; not on suggestion chips. On an input chip it replaces the remove icon
avatar string undefined Input chips: a 24dp round avatar (markup), in place of the leading icon
elevated boolean false Assist, filter and suggestion chips: the elevated style
selected boolean false Filter and input chips: whether the chip starts selected
disabled boolean false Whether the chip starts disabled
removeLabel string 'Remove {label}' Input chips: the remove button's accessible name
onRemove (chip) => void undefined Input chips: called when the chip is removed
onTrailingClick (chip) => void undefined Filter chips: gives the trailing icon its own button
trailingMenu boolean false Filter chips: the trailing button opens a menu (aria-haspopup, a drop-down arrow)
trailingLabel string '{label} options' or 'Remove {label}' Filter chips: the trailing button's accessible name
onClick (chip) => void undefined Called when the chip is activated
onChange (selected, chip) => void undefined Filter and input chips: called when a click changes the selected state
onSelect (chip) => void undefined Filter and input chips: called with the chip after onChange
ripple boolean true Whether a press shows the ripple
class string undefined Additional CSS classes
prefix string 'mtrl' Prefix for CSS class names

A set

Passed to createChips.

Option Type Default Description
chips ChipConfig[] [] The chips to build and manage
multiSelect boolean true Whether several chips can be selected at once
selectionRequired boolean false Whether the last selected chip is kept selected
label string undefined A visible label that names the set
labelPosition 'start' | 'end' 'start' Which side the label sits on
scrollable boolean false Whether the set scrolls horizontally instead of wrapping
vertical boolean false Whether the chips stack vertically
onChange (selectedValues, changedValue) => void undefined Called with every selected value and the one that changed
on { change?, add?, remove? } undefined Event handlers registered at creation
class string undefined Additional CSS classes
prefix string 'mtrl' Prefix for CSS class names

Methods

A chip

Method Parameters Returns Description
setLabel(text) / getLabel() text: string ChipComponent / string The label (setText / getText are aliases)
setLeadingIcon(icon) / getIcon() icon: string ChipComponent / string The leading icon (setIcon is an alias)
setTrailingIcon(icon) icon: string ChipComponent The trailing icon, or an input chip's remove icon
setSelected(selected) / toggleSelected() / isSelected() selected: boolean ChipComponent / boolean Selection, for filter and input chips
setValue(value) / getValue() value: string ChipComponent / string | null The chip's value
enable() / disable() / isDisabled() none ChipComponent / boolean The disabled state
getType() none ChipType The chip's type
focus() none ChipComponent Focuses the chip's action
addClass(...classes) ...classes: string[] ChipComponent Adds classes to the chip
on(event, handler) / off(event, handler) event: string, handler: Function ChipComponent Adds or removes a listener
destroy() none void Removes listeners and the element

chip.action is the chip's native button. An input chip's remove button and a filter chip's trailing button (chip.trailingAction) are its siblings, never inside it.

A set

Method Parameters Returns Description
addChip(config) / removeChip(chipOrIndex) config: ChipConfig / chipOrIndex: ChipComponent | number ChipsComponent Adds or removes a chip
getChips() / getSelectedChips() none ChipComponent[] The chips, or the selected ones
getSelectedValues() none (string | null)[] The selected values, in either mode
getValue() / setValue(values) values: string | string[] | null string | string[] | null / ChipsComponent An array when multi-select, a string or null when single-select
selectByValue(values, triggerEvent?) / clearSelection() values: string | string[], triggerEvent?: boolean ChipsComponent Programmatic selection
setScrollable(on) / setVertical(on) on: boolean ChipsComponent Layout
setLabel(text) / getLabel() text: string ChipsComponent / string The set's label
setLabelPosition(position) / getLabelPosition() position: 'start' | 'end' ChipsComponent / string Which side the label sits on
scrollToChip(chipOrIndex) chipOrIndex: ChipComponent | number ChipsComponent Scrolls a scrollable set to a chip
enableKeyboardNavigation() none ChipsComponent Enables the arrow-key navigation between chips
on(event, handler) / off(event, handler) event: string, handler: Function ChipsComponent Adds or removes a listener
destroy() none void Removes the set

Events

Event Description Data
Chip click The chip was activated { event, element, originalEvent }
Chip change A click changed the selected state { selected, chip }
Chip remove / trailing Removed, or its trailing button activated the chip
Chip focus / blur / keydown The chip's action took or lost focus, or a key went down { event, element, originalEvent }
Set change The selection changed (selectedValues, changedValue), changedValue null for programmatic changes
Set add / remove A chip was added, or is about to be removed the chip

The web component's change carries { value }, and remove the removed chip's { value }.

Accessibility

  • A set is a grid named by its visible label through aria-labelledby, with a row, and a gridcell for each chip; aria-multiselectable tells whether several can be selected.
  • The set is one Tab stop. The arrow keys move between chips, following the reading direction; Home and End go to the first and last.
  • A chip with one action is its cell, which takes focus and carries aria-selected; Space and Enter activate it. A chip with two actions keeps two native buttons in its cell, and the arrows reach both.
  • On its own, a filter or input chip is role="checkbox" with aria-checked; assist and suggestion chips are plain buttons.
  • A removed input chip's focus moves to the chip that took its place, or the previous one.
  • Chips and remove buttons have 48dp touch targets. Keyboard focus draws a 3dp focus ring, 2dp outside the chip.

Styling

Colors come from the theme's roles; --mtrl-chip-label-color, --mtrl-chip-leading-color, --mtrl-chip-trailing-color and --mtrl-chip-checkmark-color set a chip's parts.

.mtrl-chip { }
.mtrl-chip--assist, .mtrl-chip--filter, .mtrl-chip--input, .mtrl-chip--suggestion { }
.mtrl-chip--elevated, .mtrl-chip--selected, .mtrl-chip--disabled, .mtrl-chip--dragged { }
.mtrl-chip--leading, .mtrl-chip--trailing, .mtrl-chip--avatar { }  /* the parts shown */
.mtrl-chip__action { }          /* the native button */
.mtrl-chip__leading-icon, .mtrl-chip__checkmark, .mtrl-chip__label, .mtrl-chip__trailing-icon { }
.mtrl-chip__remove, .mtrl-chip__trailing-action { }

.mtrl-chips { }
.mtrl-chips--scrollable, .mtrl-chips--vertical, .mtrl-chips--with-label, .mtrl-chips--label-end { }
.mtrl-chips__container, .mtrl-chips__label { }

Measurements

From the m3.material.io chips specs and Compose's chip tokens.

Attribute Value
Height 32dp
Corner 8dp
Outline 1dp outline-variant, inside the chip; none when selected or elevated
Padding 16dp without icons, 8dp beside an icon, 4dp beside an avatar
Icon 18dp, 8dp from the label
Avatar 24dp, round
Label Label Large
Selected secondary-container
Elevated surface-container-low at elevation 1
Touch target 48dp
Motion The checkmark and leading icon expand on the fast spatial spring and fade