# Search

A text input for search queries, with a search icon. Use it to filter a list or start a search.

```tsx
// demo.tsx
import {Search} from '@earendil-works/elanor-react';

export default function SearchDemo() {
  return <Search aria-label="Search projects" placeholder="Search projects" />;
}
```

## Usage

```tsx
import {Search} from '@earendil-works/elanor-react';

<Search aria-label="Search projects" placeholder="Search projects" />;
```

The placeholder isn't a label; name the input with `aria-label` or a visible [Field](/components/field.md) label. `className` styles the input and `rootClassName` the wrapper.

## Density

Use `compact` beside compact controls, such as Dropdown filters in a [search and filter bar](/components/search-and-filter-bar.md). The touch target keeps the default height, as on compact Dropdown triggers.

```tsx
// density.tsx
import {Search, Stack} from '@earendil-works/elanor-react';

export default function SearchDensity() {
  return (
    <Stack align="stretch">
      <Search aria-label="Search projects, default" placeholder="Default" />
      <Search density="compact" aria-label="Search projects, compact" placeholder="Compact" />
    </Stack>
  );
}
```

## Filtering with a clear button

Search hides the browser's built-in clear button, so it looks the same in every browser. To add one, pass it as `endAdornment`, which places content after the input. Return focus to the input after clearing, and announce the result count in a status region.

```tsx
// filter.tsx
import {CloseIcon} from '@earendil-works/elanor-icons-react';
import {IconButton, Search, Stack, Text} from '@earendil-works/elanor-react';
import {useRef, useState} from 'react';

const projects = ['Website redesign', 'Design system', 'Release notes'];

export default function SearchFilter() {
  const [query, setQuery] = useState('');
  const inputRef = useRef<HTMLInputElement>(null);
  const matches = projects.filter((project) =>
    project.toLowerCase().includes(query.trim().toLowerCase()),
  );

  return (
    <Stack gap="medium">
      <Search
        ref={inputRef}
        aria-label="Search projects"
        placeholder="Search projects"
        value={query}
        onValueChange={setQuery}
        endAdornment={
          query ? (
            <IconButton
              aria-label="Clear search"
              onClick={() => {
                setQuery('');
                inputRef.current?.focus();
              }}
            >
              <CloseIcon />
            </IconButton>
          ) : null
        }
      />
      <Text role="status">
        {matches.length
          ? `${matches.length} matching ${matches.length === 1 ? 'project' : 'projects'}`
          : 'No matching projects.'}
      </Text>
      {matches.length > 0 ? (
        <ul>
          {matches.map((project) => (
            <li key={project}>{project}</li>
          ))}
        </ul>
      ) : null}
    </Stack>
  );
}
```

## API

### `Search`

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `type` | `'search' \| 'text'` | `'search'` |  |
| `icon` | `ReactNode` | - | Decorative icon before the input. Defaults to a search icon. |
| `endAdornment` | `ReactNode` | - | Content after the input, such as a clear button. |
| `rootClassName` | `string` | - | Class name for the outer wrapper; `className` styles the input. |
| `density` | `'compact' \| 'default'` | `'default'` | `compact` removes block padding but keeps the touch target height. |

Also accepts Base UI [`Input`](https://base-ui.com/react/components/input.md) props.
