XDSPowerSearch@xds/core · PowerSearch
Preview coming soon
Usage
PowerSearch is a structured filter bar where each token represents a field, operator, and value. Use it for complex multi-dimensional filtering when users need to combine multiple search criteria. For simple single-field search, use a text input instead.Best practices
| Guidance | Practices |
|---|---|
| Do | Define clear, descriptive field names and aliases so users can quickly find the filter they need. |
| Do | Provide a result count to give users feedback on how their filters affect the data set. |
| Don't | Use PowerSearch for simple keyword searches — a standard text input is more appropriate for single-field lookups. |
Import
tsimport {XDSPowerSearch} from '@xds/core/PowerSearch'
Props
| Prop | Type | Description |
|---|---|---|
configrequired | PowerSearchConfig | Configuration defining available fields, operators, and their value types. |
filtersrequired | ReadonlyArray<PowerSearchFilter> | Currently active filters. |
onChangerequired | (filters: ReadonlyArray<PowerSearchFilter>, changeType: PowerSearchChangeType, index: number) => void | Called when filters change. changeType is 'add', 'edit', or 'remove'. index is the affected filter's position. |
label | string (default: 'Search') | Accessible label for the search input. |
isLabelHidden | boolean (default: true) | Visually hides the label while keeping it accessible. |
placeholder | string (default: 'Search...') | Placeholder text shown when no filters are selected. |
hasAutoFocus | boolean (default: false) | Auto-focus the input on mount. |
hasClear | boolean (default: true) | Show a clear-all button for removing all filters. |
isReadOnly | boolean (default: false) | Prevent adding, editing, or removing filters. |
isDisabled | boolean (default: false) | Disables the entire component. |
status | XDSInputStatus | Validation status object with type and optional message. |
maxTokenLength | number (default: 40) | Max character length for filter value display in tokens. |
popoverSaveButtonLabel | string (default: 'Apply') | Label for the save button in the edit popover. |
timezoneID | string | Timezone ID for date formatting (e.g. "America/New_York"). |
ref | Ref<XDSPowerSearchHandle> | Imperative handle with focusTypeahead() and blurTypeahead() methods. |
endContent | ReactNode | Content to display at the end of the input row. Useful for action buttons or other controls. |
resultCount | number | string | Number of results matching the current filters. When a number, formatted as "N results". When a string, displayed as-is. |
xstyle | StyleXStyles | StyleX styles for layout customization. Must be a stylex.create() value. |
Examples
Common configurations, variations, and states.PowerSearch — Content SearchPower search with contentSearchFieldKey so free-text input maps to a title field automatically.
tsx'use client';import {useState} from 'react';import {XDSPowerSearch} from '@xds/core/PowerSearch';import type {PowerSearchConfig, PowerSearchFilter} from '@xds/core/PowerSearch';const statusValues = [{value: 'open', label: 'Open'},{value: 'in_progress', label: 'In Progress'},{value: 'closed', label: 'Closed'},];const priorityValues = [{value: 'p0', label: 'P0 — Critical'},{value: 'p1', label: 'P1 — High'},{value: 'p2', label: 'P2 — Medium'},{value: 'p3', label: 'P3 — Low'},];const config: PowerSearchConfig = {name: 'ContentSearch',contentSearchFieldKey: 'title',fields: [{key: 'title',label: 'Title',defaultOperator: 'contains',operators: [{key: 'contains', label: 'contains', value: {type: 'string'}},{key: 'not_contains',label: 'does not contain',value: {type: 'string'},},],},{key: 'status',label: 'Status',defaultOperator: 'is',operators: [{key: 'is', label: 'is', value: {type: 'enum', values: statusValues}},],},{key: 'priority',label: 'Priority',defaultOperator: 'is',operators: [{key: 'is',label: 'is',value: {type: 'enum', values: priorityValues},},],},],};export default function PowerSearchContentSearch() {const [filters, setFilters] = useState<PowerSearchFilter[]>([]);return (<XDSPowerSearchconfig={config}filters={filters}onChange={newFilters => setFilters([...newFilters])}placeholder="Type to search by title, or pick a field..."/>);}
PowerSearch — Full FeaturedPower search with multiple field types: enum, multi-select, entity, and text filters.
tsx'use client';import {useState} from 'react';import {XDSPowerSearch} from '@xds/core/PowerSearch';import type {PowerSearchConfig, PowerSearchFilter} from '@xds/core/PowerSearch';import type {XDSSearchSource, XDSSearchableItem} from '@xds/core/Typeahead';const statusValues = [{value: 'open', label: 'Open'},{value: 'in_progress', label: 'In Progress'},{value: 'review', label: 'In Review'},{value: 'closed', label: 'Closed'},];const priorityValues = [{value: 'p0', label: 'P0 — Critical'},{value: 'p1', label: 'P1 — High'},{value: 'p2', label: 'P2 — Medium'},{value: 'p3', label: 'P3 — Low'},];const users: XDSSearchableItem[] = [{id: 'user-1', label: 'Alice Johnson'},{id: 'user-2', label: 'Bob Smith'},{id: 'user-3', label: 'Charlie Brown'},{id: 'user-4', label: 'Diana Prince'},];const userSource: XDSSearchSource = {search: (q: string) =>users.filter(u => u.label.toLowerCase().includes(q.toLowerCase())),bootstrap: () => users,};const config: PowerSearchConfig = {name: 'FullSearch',fields: [{key: 'status',label: 'Status',defaultOperator: 'any_of',operators: [{key: 'any_of',label: 'is any of',value: {type: 'enum_list', values: statusValues},},{key: 'none_of',label: 'is none of',value: {type: 'enum_list', values: statusValues},},],},{key: 'title',label: 'Title',defaultOperator: 'contains',operators: [{key: 'contains', label: 'contains', value: {type: 'string'}},{key: 'not_contains',label: 'does not contain',value: {type: 'string'},},],},{key: 'priority',label: 'Priority',defaultOperator: 'is',operators: [{key: 'is', label: 'is', value: {type: 'enum', values: priorityValues}},],},{key: 'assignee',label: 'Assignee',defaultOperator: 'any_of',operators: [{key: 'any_of',label: 'is any of',value: {type: 'entity_list', searchSource: userSource},},],},],};export default function PowerSearchFullFeatured() {const [filters, setFilters] = useState<PowerSearchFilter[]>([]);return (<XDSPowerSearchconfig={config}filters={filters}onChange={newFilters => setFilters([...newFilters])}placeholder="Search..."/>);}
PowerSearch — Preset FiltersPower search initialized with pre-set filter tokens for status and priority.
tsx'use client';import {useState} from 'react';import {XDSPowerSearch} from '@xds/core/PowerSearch';import type {PowerSearchConfig, PowerSearchFilter} from '@xds/core/PowerSearch';const statusValues = [{value: 'open', label: 'Open'},{value: 'in_progress', label: 'In Progress'},{value: 'review', label: 'In Review'},{value: 'closed', label: 'Closed'},];const priorityValues = [{value: 'p0', label: 'P0 — Critical'},{value: 'p1', label: 'P1 — High'},{value: 'p2', label: 'P2 — Medium'},{value: 'p3', label: 'P3 — Low'},];const config: PowerSearchConfig = {name: 'TaskSearch',fields: [{key: 'status',label: 'Status',defaultOperator: 'is',operators: [{key: 'is', label: 'is', value: {type: 'enum', values: statusValues}},{key: 'is_not',label: 'is not',value: {type: 'enum', values: statusValues},},],},{key: 'title',label: 'Title',defaultOperator: 'contains',operators: [{key: 'contains', label: 'contains', value: {type: 'string'}},],},{key: 'priority',label: 'Priority',defaultOperator: 'is',operators: [{key: 'is',label: 'is',value: {type: 'enum', values: priorityValues},},],},],};export default function PowerSearchPresetFilters() {const [filters, setFilters] = useState<PowerSearchFilter[]>([{field: 'status', operator: 'is', value: {type: 'enum', value: 'open'}},{field: 'priority', operator: 'is', value: {type: 'enum', value: 'p1'}},]);return (<XDSPowerSearchconfig={config}filters={filters}onChange={newFilters => setFilters([...newFilters])}placeholder="Add more filters..."/>);}
PowerSearch — Search with TableComposition of PowerSearch with Table using usePowerSearchConfig to auto-generate config and filter data.
tsx'use client';import {useState} from 'react';import {XDSPowerSearch, usePowerSearchConfig} from '@xds/core/PowerSearch';import type {PowerSearchFilter} from '@xds/core/PowerSearch';import {XDSTable, pixel, proportional} from '@xds/core/Table';import type {XDSTableColumn} from '@xds/core/Table';import {XDSVStack} from '@xds/core/Layout';const genreValues = [{value: 'sci-fi', label: 'Science Fiction'},{value: 'fantasy', label: 'Fantasy'},{value: 'non-fiction', label: 'Non-Fiction'},{value: 'romance', label: 'Romance'},{value: 'mystery', label: 'Mystery'},];const fieldDefs = [{key: 'title', type: 'string', label: 'Title'},{key: 'author', type: 'string', label: 'Author'},{key: 'year', type: 'number', label: 'Publication Year'},{key: 'genre', type: 'enum', label: 'Genre', enumValues: genreValues},] as const;interface Book extends Record<string, unknown> {id: string;title: string;author: string;year: number;genre: string;}const books: Book[] = [{id: '1',title: 'Dune',author: 'Frank Herbert',year: 1965,genre: 'sci-fi',},{id: '2',title: 'Pride and Prejudice',author: 'Jane Austen',year: 1813,genre: 'romance',},{id: '3',title: '1984',author: 'George Orwell',year: 1949,genre: 'sci-fi',},{id: '4',title: 'The Hobbit',author: 'J.R.R. Tolkien',year: 1937,genre: 'fantasy',},{id: '5',title: 'Sapiens',author: 'Yuval Noah Harari',year: 2011,genre: 'non-fiction',},];const columns: XDSTableColumn<Book>[] = [{key: 'title', header: 'Title', width: proportional(2)},{key: 'author', header: 'Author', width: proportional(2)},{key: 'year', header: 'Year', width: pixel(100)},{key: 'genre',header: 'Genre',width: pixel(140),renderCell: (book: Book) =>genreValues.find(g => g.value === book.genre)?.label ?? book.genre,},];export default function PowerSearchSearchWithTable() {const [filters, setFilters] = useState<PowerSearchFilter[]>([]);const {config, applyFilters} = usePowerSearchConfig(fieldDefs, 'Books');const filteredBooks = applyFilters(filters, books);return (<XDSVStack gap={4}><XDSPowerSearchconfig={config}filters={filters}onChange={newFilters => setFilters([...newFilters])}placeholder="Filter books by title, author, year, genre..."resultCount={filteredBooks.length}/><XDSTable data={filteredBooks} columns={columns} idKey="id" hasHover /></XDSVStack>);}
Showcase source
tsx'use client';import {useState} from 'react';import {XDSPowerSearch} from '@xds/core/PowerSearch';import type {PowerSearchConfig, PowerSearchFilter} from '@xds/core/PowerSearch';const config: PowerSearchConfig = {name: 'BasicSearch',fields: [{key: 'status',label: 'Status',defaultOperator: 'is',operators: [{key: 'is',label: 'is',value: {type: 'enum',values: [{value: 'open', label: 'Open'},{value: 'in_progress', label: 'In Progress'},{value: 'closed', label: 'Closed'},],},},],},{key: 'title',label: 'Title',defaultOperator: 'contains',operators: [{key: 'contains', label: 'contains', value: {type: 'string'}},],},],};const initialFilters: PowerSearchFilter[] = [{field: 'status', operator: 'is', value: {type: 'enum', value: 'open'}},{field: 'title',operator: 'contains',value: {type: 'string', value: 'dashboard'},},];export default function PowerSearchShowcase() {const [filters, setFilters] = useState<PowerSearchFilter[]>(initialFilters);return (<XDSPowerSearchconfig={config}filters={filters}onChange={newFilters => setFilters([...newFilters])}placeholder="Search by status, title..."/>);}