Combobox with tags
This pattern is used when you need multiple items to be selected after searching a server side, paginated list. The tags retain user selections because server side searches wipe the user selection in the combobox.
const simulateFetch = (ms) => new Promise((r) => setTimeout(r, ms));const exampleItemsFromTheServer = React.useMemo( () => [ { label: 'Banana', value: 'banana' }, { label: 'Blueberry', value: 'blueberry' }, { label: 'Bubble Gum', value: 'bubble-gum' }, { label: 'Chocolate', value: 'chocolate' }, { label: 'Coconut', value: 'coconut' }, { label: 'Coffee', value: 'coffee' }, { label: 'Cookie Dough', value: 'cookie-dough' }, { label: 'Cotton Candy', value: 'cotton-candy' }, { label: 'Mango', value: 'mango' }, { label: 'Matcha', value: 'matcha' }, { label: 'Mint', value: 'mint' }, { label: 'Oreo', value: 'oreo' }, { label: 'Peanut Butter', value: 'peanut-butter' }, { label: 'Pistachio', value: 'pistachio' }, { label: 'Pumpkin', value: 'pumpkin' }, { label: 'Salted Caramel', value: 'salted-caramel' }, { label: 'Strawberry', value: 'strawberry' }, { label: 'Vanilla', value: 'vanilla' }, ], []);const [loadingState, setLoadingState] = useState('idle');const [inputValue, setInputValue] = useState('');const [items, setItems] = useState([]);const [selectedItem, setSelectedItem] = useState(null);const onInputChange = (text) => { setInputValue(text);};// fake server callReact.useEffect(() => { const fetchData = async () => { // no input if (!inputValue) { setItems([]); return; } // set the loading state setLoadingState('loading'); // simulate fetch await simulateFetch(Math.random() * 1000); setItems(() => { if (inputValue) { return exampleItemsFromTheServer.filter(({ label }) => label.toLowerCase().includes(inputValue.toLowerCase()) ); } return exampleItemsFromTheServer; }); // reset the loading state after fetching setLoadingState('idle'); }; fetchData();}, [inputValue]);const handleChange = (newItem) => { setSelectedItem((items) => { if (!items?.find((item) => item.value === newItem.value)) { return [...(items || []), newItem]; } return items; }); setInputValue('');};return ( <Stack gap="small"> <Field label="Flavors"> <Combobox placeholder="Start typing to find your favorite flavor" loadingState={loadingState} inputValue={inputValue} items={items} onChange={handleChange} onInputChange={onInputChange} value={null} /> </Field> <Inline gap="small"> {selectedItem?.map((item) => ( <Tag key={item.value} label={item.label} onRemove={() => { setSelectedItem((selectedItems) => selectedItems.filter( (selectedItem) => selectedItem.value !== item.value ) ); }} /> ))} </Inline> </Stack>);