Skip to main content
The Search component provides a specialized input field for search functionality with a built-in search icon and optional clear button.

Basic usage

import { Search } from '@raystack/apsara';

function App() {
  return <Search placeholder="Search..." />;
}

Props

Inherits all props from InputField except leadingIcon.
placeholder
string
default:"'Search'"
Placeholder text for the search input.
value
string
The controlled value of the search input.
onChange
(event: React.ChangeEvent<HTMLInputElement>) => void
Callback fired when the input value changes.
showClearButton
boolean
Whether to show the clear button when there is a value.
onClear
() => void
Callback fired when the clear button is clicked.
variant
'default' | 'borderless'
default:"'default'"
The visual variant of the search input.
size
'small' | 'large'
The size of the search input.
disabled
boolean
Whether the search input is disabled.
width
string | number
default:"'100%'"
Width of the search input.
className
string
Additional CSS classes to apply to the search input.
function ControlledSearch() {
  const [query, setQuery] = useState('');

  return (
    <Search
      value={query}
      onChange={(e) => setQuery(e.target.value)}
      placeholder="Search items..."
    />
  );
}

With clear button

function SearchWithClear() {
  const [query, setQuery] = useState('');

  return (
    <Search
      value={query}
      onChange={(e) => setQuery(e.target.value)}
      showClearButton
      onClear={() => setQuery('')}
      placeholder="Search..."
    />
  );
}

Size variants

<Search size="small" placeholder="Small search" />
<Search size="large" placeholder="Large search" />

Borderless variant

<Search variant="borderless" placeholder="Borderless search" />

Custom width

<Search width="300px" placeholder="Fixed width search" />

Disabled state

<Search disabled placeholder="Disabled search" />

Search with live results

function LiveSearch() {
  const [query, setQuery] = useState('');
  const [results, setResults] = useState([]);

  const handleSearch = (e) => {
    const value = e.target.value;
    setQuery(value);
    
    // Simulate search
    if (value) {
      const filtered = items.filter(item =>
        item.toLowerCase().includes(value.toLowerCase())
      );
      setResults(filtered);
    } else {
      setResults([]);
    }
  };

  return (
    <div>
      <Search
        value={query}
        onChange={handleSearch}
        showClearButton
        onClear={() => {
          setQuery('');
          setResults([]);
        }}
        placeholder="Search items..."
      />
      {results.length > 0 && (
        <ul>
          {results.map((result, i) => (
            <li key={i}>{result}</li>
          ))}
        </ul>
      )}
    </div>
  );
}

Search with debouncing

function DebouncedSearch() {
  const [query, setQuery] = useState('');
  const [debouncedQuery, setDebouncedQuery] = useState('');

  useEffect(() => {
    const timer = setTimeout(() => {
      setDebouncedQuery(query);
    }, 300);

    return () => clearTimeout(timer);
  }, [query]);

  useEffect(() => {
    if (debouncedQuery) {
      // Perform search with debouncedQuery
      console.log('Searching for:', debouncedQuery);
    }
  }, [debouncedQuery]);

  return (
    <Search
      value={query}
      onChange={(e) => setQuery(e.target.value)}
      showClearButton
      onClear={() => setQuery('')}
      placeholder="Type to search..."
    />
  );
}

Search bar with filters

function SearchWithFilters() {
  const [query, setQuery] = useState('');
  const [filter, setFilter] = useState('all');

  return (
    <div style={{ display: 'flex', gap: '1rem' }}>
      <Search
        value={query}
        onChange={(e) => setQuery(e.target.value)}
        showClearButton
        onClear={() => setQuery('')}
        placeholder="Search..."
        width="300px"
      />
      <Select value={filter} onValueChange={setFilter}>
        <Select.Trigger size="small">
          <Select.Value />
        </Select.Trigger>
        <Select.Content>
          <Select.Item value="all">All</Select.Item>
          <Select.Item value="active">Active</Select.Item>
          <Select.Item value="archived">Archived</Select.Item>
        </Select.Content>
      </Select>
    </div>
  );
}

Searchable table

function SearchableTable() {
  const [search, setSearch] = useState('');
  const data = [...]; // your data
  
  const filteredData = data.filter(row =>
    Object.values(row).some(value =>
      String(value).toLowerCase().includes(search.toLowerCase())
    )
  );

  return (
    <div>
      <Search
        value={search}
        onChange={(e) => setSearch(e.target.value)}
        showClearButton
        onClear={() => setSearch('')}
        placeholder="Search table..."
        size="small"
      />
      <table>
        {/* render filteredData */}
      </table>
    </div>
  );
}

Accessibility

  • The Search component has role="search" for proper semantic meaning.
  • The magnifying glass icon is automatically included as a visual indicator.
  • The input has an aria-label set to the placeholder value.
  • The clear button has aria-label="Clear search" for screen reader users.
  • Keyboard navigation is fully supported.
  • The clear button can be activated with Enter or Space when focused.