Skip to main content

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"'
/>
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

With Clear Button

<Search 
  placeholder="Search..."
  showClearButton // Default: true
/>

Without Clear Button

<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
string
Label text displayed above the search input.
placeholder
string
default:"'Search...'"
Placeholder text shown when the input is empty.
helperText
string
Helper text displayed below the input.
size
'sm' | 'md' | 'lg'
default:"'md'"
Size of the search input.
showClearButton
boolean
default:"true"
Whether to show the clear button when input has text.
onClear
() => void
Handler called when the clear button is clicked.
value
string
Value for controlled input.
onValueChange
(value: string) => void
Handler called when the input value changes.
isDisabled
boolean
default:"false"
Whether the search input is disabled.

Live Search Props

Enable live search functionality with results dropdown.
Function to perform the search. Can be async.
searchResults
SearchResult[]
External search results to display (for controlled component).
onResultSelect
(result: SearchResult) => void
Handler called when a result is selected.
isLoading
boolean
default:"false"
Whether search is in progress (shows loading indicator).
minSearchLength
number
default:"2"
Minimum characters required before triggering search.
searchDelay
number
default:"300"
Debounce delay in milliseconds before triggering search.
noResultsText
string
default:"'No results found'"
Text displayed when search returns no results.

Highlighting Props

enableHighlighting
boolean
default:"true"
Enable highlighting of search terms in results.
highlightClassName
string
Custom CSS classes for highlighted text. Default uses yellow background.

Rendering Props

renderResult
(result: SearchResult) => React.ReactNode
Custom render function for search results.
showPopover
boolean
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

  1. Provide clear placeholder text:
    <Search placeholder="Search products by name or SKU..." />
    
  2. Use helper text for search tips:
    <Search 
      helperText='Use quotes for exact matches'
      placeholder="Search..."
    />
    
  3. Set appropriate minimum search length:
    <Search 
      enableLiveSearch
      minSearchLength={3} // Avoid too many results
      onSearch={handleSearch}
    />
    

Examples

<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}
    />
  );
}
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);
      }}
    />
  );
}

Build docs developers (and LLMs) love