Dropdown with Chips
Overview
Section titled “Overview”A multi-select built on top of the Dropdown component. Each selected option is mirrored as a removable chip below the trigger, so the current selection stays visible without opening the menu.
- Multi-select via the Dropdown’s
multipleprop - Removable chips for every selection, with a one-click remove
- Per-option visuals (here, country flags) driven by a shortcode map
How to download
Section titled “How to download”npx gameface-cli add ChipMultiSelectThe CLI pulls in whatever ChipMultiSelect depends on. To refresh an existing copy, update it instead.
The component is driven by two-way binding. Provide a value signal array and the setter on the onChange callback to receive updates:
import { createSignal } from 'solid-js';import ChipMultiSelect from '@recipes/ChipMultiSelect/ChipMultiSelect';
const App = () => { const [selectedCountries, setSelectedCountries] = createSignal<string[]>(['br', 'jp']);
return ( <div style={{width: '15rem'}}> <ChipMultiSelect value={selectedCountries()} onChange={setSelectedCountries} /> </div> )}selectedCountries() is the single source of truth: the dropdown renders it as chips, and every selection - from the options panel or from a chip’s remove button - comes back through onChange. That is the same wiring the Filterable Data Table uses, where the array lives in the filter draft store instead of a signal:
<ChipMultiSelect value={draft.nationality} onChange={setNationalityFilter} />How it works
Section titled “How it works”The recipe is two pieces stacked in a wrapper:
- The Dropdown in
multiplemode. The placeholder shows a count (4 selected) instead of a single value, and each option renders its label plus a small image. - The chips row, driven by the same
valuearray. Each chip’s remove button callsdropdownRef.deselectOption(value)to sync the selection back through the dropdown.
State lives outside the component — you pass value in and get changes back through onChange:
const [selected, setSelected] = createSignal<string[]>([]);
<ChipMultiSelect value={selected()} onChange={setSelected} />Wiring up real images
Section titled “Wiring up real images”To keep the recipe asset-free, options and chips render a placeholder — a gray box with the country’s shortcode:
<Flex justify-content="center" align-items="center" class={styles['dropdown-option-content-image']} style={{ 'background-color': 'gray', 'color': 'white', 'text-align': 'center' }}>{country.code}</Flex>To use real flags, swap that for the BackgroundImage block already sitting commented out next to it, in both places (the option and the chip):
<BackgroundImage class={styles['dropdown-option-content-image']} src={FLAG_SRC[country.code]} />FLAG_SRC is built in utils/imageMap.ts — it globs an assets folder and maps each file’s name (the shortcode) to its URL:
const flagModules = import.meta.glob('@assets/icons/flags/*.svg', { eager: true });
const FLAG_SRC: Record<string, string> = {};for (const path in flagModules) { const code = path.slice(path.lastIndexOf('/') + 1).replace('.svg', ''); FLAG_SRC[code] = flagModules[path].default;}So the only thing you need to provide is the assets themselves: drop SVGs into @assets/icons/flags/, named by the shortcode used in utils/countriesList.ts (ar.svg, br.svg, jp.svg, …). The map wires them up automatically — no manual imports.
Customizing
Section titled “Customizing”- Options — edit
utils/countriesList.ts; each entry is{ name, code }. - Placeholder text — change the
Dropdown.Placeholdercontent (e.g.${value.length} selected/Any). - Colors & sizing — the look is driven by the variables at the top of
ChipMultiSelect.module.scss($interactive,$bg-elevated, radius, chip/image sizes).
© 2026 Coherent Labs. All rights reserved.