tsimport {XDSTokenizer} from '@xds/core/Tokenizer'
| Guidance | Practices |
|---|---|
| Do | Write a placeholder that tells users what they can search for — "Search people…" or "Add tags…" — so the input is not a blank mystery. |
| Do | Set maxEntries when the number of selections should be bounded, like limiting a review to 5 approvers. |
| Do | Use hasCreate for free-form tagging where users need to enter values that do not exist in the search source. |
| Do | Show validation status with the status prop so users know immediately when a selection is missing or invalid. |
| Don't | Don’t use Tokenizer for single-item selection — use Typeahead instead. Tokenizer is for building sets of two or more items. |
| Don't | Avoid applying custom colors to individual tokens inside a Tokenizer — use the default token style for visual consistency across the set. |
| Don't | Don’t hide the label — every Tokenizer needs a visible label so users understand what they are selecting. Use isLabelHidden only when surrounding context makes the purpose obvious. |
tsx'use client';import {useState} from 'react';import * as stylex from '@stylexjs/stylex';import {XDSTokenizer} from '@xds/core/Tokenizer';import {XDSStack} from '@xds/core/Layout';import {XDSText} from '@xds/core/Text';import type {XDSSearchableItem, XDSSearchSource} from '@xds/core/Typeahead';const styles = stylex.create({fixed: {width: 400},});const users: XDSSearchableItem[] = [{id: '1', label: 'Alice Johnson'},{id: '2', label: 'Bob Smith'},{id: '3', label: 'Charlie Brown'},{id: '4', label: 'Diana Prince'},{id: '5', label: 'Eve Williams'},];const userSource: XDSSearchSource = {search: (query: string) =>users.filter(u => u.label.toLowerCase().includes(query.toLowerCase())),bootstrap: () => users,};export default function TokenizerClear() {const [value, setValue] = useState<XDSSearchableItem[]>([users[0], users[1]]);return (<XDSStack direction="vertical" gap={2}><XDSText type="supporting" color="secondary">Clear-all button appears when tokens are selected</XDSText><XDSTokenizerlabel="Team Members"placeholder="Search people..."searchSource={userSource}value={value}onChange={items => setValue(items)}hasClearxstyle={styles.fixed}/></XDSStack>);}
tsx'use client';import {useState} from 'react';import * as stylex from '@stylexjs/stylex';import {XDSTokenizer} from '@xds/core/Tokenizer';import {XDSStack} from '@xds/core/Layout';import {XDSText} from '@xds/core/Text';import type {XDSSearchableItem, XDSSearchSource} from '@xds/core/Typeahead';const styles = stylex.create({fixed: {width: 400},});const emptySource: XDSSearchSource = {search: () => [],bootstrap: () => [],};const users: XDSSearchableItem[] = [{id: '1', label: 'Alice Johnson'},{id: '2', label: 'Bob Smith'},{id: '3', label: 'Charlie Brown'},{id: '4', label: 'Diana Prince'},{id: '5', label: 'Eve Williams'},];const userSource: XDSSearchSource = {search: (query: string) =>users.filter(u => u.label.toLowerCase().includes(query.toLowerCase())),bootstrap: () => users,};export default function TokenizerCreatable() {const [tags, setTags] = useState<XDSSearchableItem[]>([]);const [members, setMembers] = useState<XDSSearchableItem[]>([]);return (<XDSStack direction="vertical" gap={4}><XDSStack direction="vertical" gap={1}><XDSText type="supporting" color="secondary">Free-text only</XDSText><XDSTokenizerlabel="Tags"searchSource={emptySource}value={tags}onChange={items => setTags(items)}hasCreateplaceholder="Type a tag and press Enter..."xstyle={styles.fixed}/></XDSStack><XDSStack direction="vertical" gap={1}><XDSText type="supporting" color="secondary">Create or search</XDSText><XDSTokenizerlabel="Team Members"searchSource={userSource}value={members}onChange={items => setMembers(items)}hasCreatehasEntriesOnFocusplaceholder="Search or type a new name..."xstyle={styles.fixed}/></XDSStack></XDSStack>);}
tsx'use client';import {useState} from 'react';import * as stylex from '@stylexjs/stylex';import {XDSTokenizer} from '@xds/core/Tokenizer';import {XDSButton} from '@xds/core/Button';import {XDSStack} from '@xds/core/Layout';import {XDSText} from '@xds/core/Text';import type {XDSSearchableItem, XDSSearchSource} from '@xds/core/Typeahead';const styles = stylex.create({fixed: {width: 400},});const users: XDSSearchableItem[] = [{id: '1', label: 'Alice Johnson'},{id: '2', label: 'Bob Smith'},{id: '3', label: 'Charlie Brown'},{id: '4', label: 'Diana Prince'},{id: '5', label: 'Eve Williams'},{id: '6', label: 'Frank Miller'},];const userSource: XDSSearchSource = {search: (query: string) =>users.filter(u => u.label.toLowerCase().includes(query.toLowerCase())),bootstrap: () => users,};export default function TokenizerEndContent() {const [value, setValue] = useState<XDSSearchableItem[]>([users[0], users[2]]);return (<XDSStack direction="vertical" gap={2}><XDSText type="supporting" color="secondary">Action button in the end slot</XDSText><XDSTokenizerlabel="Team Members"placeholder="Search people..."searchSource={userSource}value={value}onChange={items => setValue(items)}endContent={<XDSButton label="Apply" variant="primary" size="sm" />}xstyle={styles.fixed}/></XDSStack>);}
tsx'use client';import {useState} from 'react';import * as stylex from '@stylexjs/stylex';import {XDSTokenizer} from '@xds/core/Tokenizer';import {XDSStack} from '@xds/core/Layout';import {XDSText} from '@xds/core/Text';import {MagnifyingGlassIcon} from '@heroicons/react/24/outline';import type {XDSSearchableItem, XDSSearchSource} from '@xds/core/Typeahead';const styles = stylex.create({fixed: {width: 400},});const users: XDSSearchableItem[] = [{id: '1', label: 'Alice Johnson'},{id: '2', label: 'Bob Smith'},{id: '3', label: 'Charlie Brown'},{id: '4', label: 'Diana Prince'},{id: '5', label: 'Eve Williams'},];const userSource: XDSSearchSource = {search: (query: string) =>users.filter(u => u.label.toLowerCase().includes(query.toLowerCase())),bootstrap: () => users,};export default function TokenizerIcon() {const [value, setValue] = useState<XDSSearchableItem[]>([users[0], users[2]]);return (<XDSStack direction="vertical" gap={2}><XDSText type="supporting" color="secondary">Leading icon reinforces the search affordance</XDSText><XDSTokenizerlabel="Team Members"placeholder="Search people..."searchSource={userSource}value={value}onChange={items => setValue(items)}startIcon={MagnifyingGlassIcon}xstyle={styles.fixed}/></XDSStack>);}
tsx'use client';import {useState} from 'react';import * as stylex from '@stylexjs/stylex';import {XDSTokenizer} from '@xds/core/Tokenizer';import {XDSStack} from '@xds/core/Layout';import {XDSText} from '@xds/core/Text';import type {XDSSearchableItem, XDSSearchSource} from '@xds/core/Typeahead';const styles = stylex.create({fixed: {width: 400},});const skills: XDSSearchableItem[] = [{id: '1', label: 'React'},{id: '2', label: 'TypeScript'},{id: '3', label: 'GraphQL'},{id: '4', label: 'Node.js'},{id: '5', label: 'Python'},{id: '6', label: 'Rust'},{id: '7', label: 'Go'},{id: '8', label: 'Swift'},];const skillSource: XDSSearchSource = {search: (query: string) =>skills.filter(s => s.label.toLowerCase().includes(query.toLowerCase())),bootstrap: () => skills,};const MAX_SKILLS = 3;export default function TokenizerMaxEntries() {const [value, setValue] = useState<XDSSearchableItem[]>([skills[0],skills[1],]);return (<XDSStack direction="vertical" gap={2}><XDSText type="supporting" color="secondary">Limited to {MAX_SKILLS} selections — {MAX_SKILLS - value.length}{' '}remaining</XDSText><XDSTokenizerlabel="Top Skills"placeholder="Search skills..."description={`Choose up to ${MAX_SKILLS} skills`}searchSource={skillSource}value={value}onChange={items => setValue(items)}maxEntries={MAX_SKILLS}xstyle={styles.fixed}/></XDSStack>);}
tsx'use client';import {useState} from 'react';import * as stylex from '@stylexjs/stylex';import {XDSTokenizer} from '@xds/core/Tokenizer';import {XDSStack} from '@xds/core/Layout';import {XDSText} from '@xds/core/Text';import type {XDSSearchableItem, XDSSearchSource} from '@xds/core/Typeahead';const styles = stylex.create({fixed: {width: 400, maxWidth: 400},});const users: XDSSearchableItem[] = [{id: '1', label: 'Alice Johnson'},{id: '2', label: 'Bob Smith'},{id: '3', label: 'Charlie Brown'},{id: '4', label: 'Diana Prince'},{id: '5', label: 'Eve Williams'},{id: '6', label: 'Frank Miller'},];const userSource: XDSSearchSource = {search: (query: string) =>users.filter(u => u.label.toLowerCase().includes(query.toLowerCase())),bootstrap: () => users,};export default function TokenizerOverflow() {const [inlineValue, setInlineValue] = useState<XDSSearchableItem[]>(users);const [layerValue, setLayerValue] = useState<XDSSearchableItem[]>(users);return (<XDSStack direction="vertical" gap={4}><XDSStack direction="vertical" gap={1}><XDSText type="supporting" color="secondary">Inline overflow — content shifts down on expand</XDSText><XDSTokenizerlabel="Inline Overflow"placeholder="Add more..."searchSource={userSource}value={inlineValue}onChange={items => setInlineValue(items)}tokenOverflowBehavior="unfocusedInline"xstyle={styles.fixed}/></XDSStack><XDSStack direction="vertical" gap={1}><XDSText type="supporting" color="secondary">Layer overflow — expands as overlay, no layout shift</XDSText><XDSTokenizerlabel="Layer Overflow"placeholder="Add more..."searchSource={userSource}value={layerValue}onChange={items => setLayerValue(items)}tokenOverflowBehavior="unfocusedLayer"xstyle={styles.fixed}/></XDSStack></XDSStack>);}
tsx'use client';import {useState} from 'react';import * as stylex from '@stylexjs/stylex';import {XDSTokenizer} from '@xds/core/Tokenizer';import {XDSStack} from '@xds/core/Layout';import type {XDSSearchableItem, XDSSearchSource} from '@xds/core/Typeahead';const styles = stylex.create({fixed: {width: 400},});const users: XDSSearchableItem[] = [{id: '1', label: 'Alice Johnson'},{id: '2', label: 'Bob Smith'},{id: '3', label: 'Charlie Brown'},{id: '4', label: 'Diana Prince'},{id: '5', label: 'Eve Williams'},];const userSource: XDSSearchSource = {search: (query: string) =>users.filter(u => u.label.toLowerCase().includes(query.toLowerCase())),bootstrap: () => users,};export default function TokenizerStates() {const [errorValue, setErrorValue] = useState<XDSSearchableItem[]>([]);const [warningValue, setWarningValue] = useState<XDSSearchableItem[]>([users[0],]);const [successValue, setSuccessValue] = useState<XDSSearchableItem[]>([users[1],users[3],]);return (<XDSStack direction="vertical" gap={4}><XDSTokenizerlabel="Disabled field"searchSource={userSource}value={[users[0], users[2]]}onChange={() => {}}isDisabledxstyle={styles.fixed}/><XDSTokenizerlabel="Error message"placeholder="Search people..."searchSource={userSource}value={errorValue}onChange={items => setErrorValue(items)}isRequiredstatus={{type: 'error', message: 'At least one reviewer is required'}}xstyle={styles.fixed}/><XDSTokenizerlabel="Warning message"placeholder="Search people..."searchSource={userSource}value={warningValue}onChange={items => setWarningValue(items)}status={{type: 'warning',message: 'Consider adding at least 2 approvers',}}xstyle={styles.fixed}/><XDSTokenizerlabel="Success message"placeholder="Search people..."searchSource={userSource}value={successValue}onChange={items => setSuccessValue(items)}status={{type: 'success', message: 'All required reviewers added'}}xstyle={styles.fixed}/></XDSStack>);}
tsx'use client';import * as stylex from '@stylexjs/stylex';import {XDSTokenizer} from '@xds/core/Tokenizer';import type {XDSSearchSource} from '@xds/core/Typeahead';const styles = stylex.create({fixed: {width: 400},});const source: XDSSearchSource = {search: () => [],bootstrap: () => [],};export default function TokenizerShowcase() {return (<XDSTokenizerlabel="Tags"placeholder="Search..."searchSource={source}value={[{id: '1', label: 'Design'},{id: '2', label: 'Engineering'},]}onChange={() => {}}xstyle={styles.fixed}/>);}