Overview
The Search component provides an advanced search experience with optional live search functionality, result highlighting, loading states, and customizable result rendering. Built on React Aria Components for full accessibility.
Import
import { Search } from 'stride-ds';
import type { SearchResult } from 'stride-ds';
Basic Usage
import { Search } from 'stride-ds';
function Example() {
return <Search placeholder="Search for anything..." />;
}
Sizes
Three size options are available:
<Search size="sm" placeholder="Quick search..." />
<Search size="md" placeholder="Search here..." />
<Search size="lg" placeholder="Search for products, articles, or help..." />
With Label and Helper Text
<Search
label="Global Search"
placeholder="Search across all content..."
helperText='Use quotes for exact matches, e.g. "design system"'
/>
Live Search
Enable live search with results dropdown:
import { Search, SearchResult } from 'stride-ds';
import { useState } from 'react';
function LiveSearchExample() {
const handleSearch = async (query: string): Promise<SearchResult[]> => {
const response = await fetch(`/api/search?q=${query}`);
return response.json();
};
return (
<Search
placeholder="Search..."
enableLiveSearch
onSearch={handleSearch}
onResultSelect={(result) => {
console.log('Selected:', result);
}}
minSearchLength={2}
searchDelay={300}
/>
);
}
Result Highlighting
Highlight search terms in results:
<Search
placeholder="Search with highlighting..."
enableLiveSearch
enableHighlighting // Default: true with yellow background
onSearch={handleSearch}
/>
Custom Highlighting
<Search
placeholder="Custom highlight color..."
enableLiveSearch
enableHighlighting
highlightClassName="bg-blue-200 text-blue-900 px-1 rounded font-bold"
onSearch={handleSearch}
/>
Disable Highlighting
<Search
placeholder="No highlighting..."
enableLiveSearch
enableHighlighting={false}
onSearch={handleSearch}
/>
States
<Search
placeholder="Search..."
showClearButton // Default: true
/>
<Search
placeholder="Search without clear..."
showClearButton={false}
/>
Disabled
<Search
placeholder="Search is disabled..."
isDisabled
defaultValue="Cannot search right now"
/>
Loading State
<Search
placeholder="Search..."
enableLiveSearch
isLoading
onSearch={handleSearch}
/>
Props
Label text displayed above the search input.
placeholder
string
default:"'Search...'"
Placeholder text shown when the input is empty.
Helper text displayed below the input.
size
'sm' | 'md' | 'lg'
default:"'md'"
Size of the search input.
Whether to show the clear button when input has text.
Handler called when the clear button is clicked.
Value for controlled input.
Handler called when the input value changes.
Whether the search input is disabled.
Live Search Props
Enable live search functionality with results dropdown.
onSearch
(query: string) => Promise<SearchResult[]> | SearchResult[]
Function to perform the search. Can be async.
External search results to display (for controlled component).
onResultSelect
(result: SearchResult) => void
Handler called when a result is selected.
Whether search is in progress (shows loading indicator).
Minimum characters required before triggering search.
Debounce delay in milliseconds before triggering search.
noResultsText
string
default:"'No results found'"
Text displayed when search returns no results.
Highlighting Props
Enable highlighting of search terms in results.
Custom CSS classes for highlighted text. Default uses yellow background.
Rendering Props
renderResult
(result: SearchResult) => React.ReactNode
Custom render function for search results.
Manually control popover visibility (overrides default behavior).
SearchResult Type
interface SearchResult {
id: string; // Unique identifier
title: string; // Main title (required)
description?: string; // Optional description
category?: string; // Optional category label
metadata?: Record<string, unknown>; // Additional data
}
Accessibility
The Search component provides full accessibility:
- Keyboard Navigation: Standard text input navigation, Arrow keys for results
- Focus Management: Proper focus handling in results dropdown
- Screen Reader Support: Proper ARIA attributes and announcements
- Loading States: Loading indicators are announced
Best Practices
-
Provide clear placeholder text:
<Search placeholder="Search products by name or SKU..." />
-
Use helper text for search tips:
<Search
helperText='Use quotes for exact matches'
placeholder="Search..."
/>
-
Set appropriate minimum search length:
<Search
enableLiveSearch
minSearchLength={3} // Avoid too many results
onSearch={handleSearch}
/>
Examples
Basic Search
<Search
label="Search Products"
placeholder="Enter product name or keywords..."
/>
All Sizes
<div className="space-y-4">
<Search
label="Small Search"
size="sm"
placeholder="Small search input..."
/>
<Search
label="Medium Search"
size="md"
placeholder="Medium search input..."
/>
<Search
label="Large Search"
size="lg"
placeholder="Large search input..."
/>
</div>
With Custom Handlers
import { useState } from 'react';
import { Search } from 'stride-ds';
function CustomHandlersExample() {
const [searchValue, setSearchValue] = useState('');
return (
<Search
label="Advanced Search"
placeholder="Search with custom handlers..."
value={searchValue}
onValueChange={(value) => {
setSearchValue(value);
if (value.length > 2) {
console.log('Searching for:', value);
}
}}
onClear={() => {
console.log('Search cleared');
}}
/>
);
}
Live Search with Mock Data
import { Search, SearchResult } from 'stride-ds';
const mockData: SearchResult[] = [
{
id: '1',
title: 'React Components',
description: 'Reusable UI components built with React',
category: 'Development',
},
{
id: '2',
title: 'Design System',
description: 'Complete design system documentation',
category: 'Design',
},
// ... more results
];
function LiveSearchExample() {
const handleSearch = (query: string): SearchResult[] => {
return mockData.filter(
(item) =>
item.title.toLowerCase().includes(query.toLowerCase()) ||
item.description?.toLowerCase().includes(query.toLowerCase())
);
};
return (
<Search
placeholder="Search documentation..."
enableLiveSearch
onSearch={handleSearch}
onResultSelect={(result) => {
console.log('Selected:', result);
// Navigate to result or perform action
}}
minSearchLength={2}
/>
);
}
Custom Result Rendering
import { Search, SearchResult } from 'stride-ds';
import { FileText, Code, Book } from 'lucide-react';
function CustomRenderExample() {
const getCategoryIcon = (category: string) => {
switch (category) {
case 'Development': return <Code size={16} />;
case 'Design': return <FileText size={16} />;
default: return <Book size={16} />;
}
};
const renderResult = (result: SearchResult) => (
<div className="flex items-start gap-3">
<div className="mt-1">
{getCategoryIcon(result.category || '')}
</div>
<div>
<div className="font-semibold">{result.title}</div>
<div className="text-sm text-gray-600">{result.description}</div>
{result.category && (
<div className="text-xs text-gray-500 mt-1">
{result.category}
</div>
)}
</div>
</div>
);
return (
<Search
placeholder="Search with custom rendering..."
enableLiveSearch
onSearch={handleSearch}
renderResult={renderResult}
/>
);
}
Controlled Search
import { useState } from 'react';
import { Search } from 'stride-ds';
function ControlledExample() {
const [value, setValue] = useState('');
return (
<div>
<Search
placeholder="Type to search..."
value={value}
onValueChange={setValue}
/>
<p className="mt-4 text-sm">
Current value: "{value}"
</p>
</div>
);
}
Async Search with Loading
import { Search, SearchResult } from 'stride-ds';
import { useState } from 'react';
function AsyncSearchExample() {
const [isLoading, setIsLoading] = useState(false);
const handleSearch = async (query: string): Promise<SearchResult[]> => {
setIsLoading(true);
try {
const response = await fetch(`/api/search?q=${encodeURIComponent(query)}`);
const results = await response.json();
return results;
} finally {
setIsLoading(false);
}
};
return (
<Search
placeholder="Search products..."
enableLiveSearch
isLoading={isLoading}
onSearch={handleSearch}
onResultSelect={(result) => {
console.log('Selected:', result);
}}
/>
);
}