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

GuidancePractices
DoDefine clear, descriptive field names and aliases so users can quickly find the filter they need.
DoProvide a result count to give users feedback on how their filters affect the data set.
Don'tUse PowerSearch for simple keyword searches — a standard text input is more appropriate for single-field lookups.

Import

ts
import {XDSPowerSearch} from '@xds/core/PowerSearch'

Props

PropTypeDescription
configrequired
PowerSearchConfigConfiguration defining available fields, operators, and their value types.
filtersrequired
ReadonlyArray<PowerSearchFilter>Currently active filters.
onChangerequired
(filters: ReadonlyArray<PowerSearchFilter>, changeType: PowerSearchChangeType, index: number) => voidCalled 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
XDSInputStatusValidation 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
stringTimezone ID for date formatting (e.g. "America/New_York").
ref
Ref<XDSPowerSearchHandle>Imperative handle with focusTypeahead() and blurTypeahead() methods.
endContent
ReactNodeContent to display at the end of the input row. Useful for action buttons or other controls.
resultCount
number | stringNumber of results matching the current filters. When a number, formatted as "N results". When a string, displayed as-is.
xstyle
StyleXStylesStyleX 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 (
<XDSPowerSearch
config={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 (
<XDSPowerSearch
config={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 (
<XDSPowerSearch
config={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}>
<XDSPowerSearch
config={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 (
<XDSPowerSearch
config={config}
filters={filters}
onChange={newFilters => setFilters([...newFilters])}
placeholder="Search by status, title..."
/>
);
}