Buttons
Data Display
Layout
Navigation
Search Input
Search field with a search icon and a button that clears the value.
React
import { Icon } from "@isbatak/react-ui/icon"
import { SearchInput } from "@isbatak/react-ui/search-input"
export function SearchInputDemo() {
return (
<SearchInput.Root maxW="xs">
<SearchInput.Label>Search</SearchInput.Label>
<SearchInput.Control>
<SearchInput.Icon>
<Icon name="search" />
</SearchInput.Icon>
<SearchInput.Input placeholder="Search components" />
<SearchInput.ClearTrigger>
<Icon name="close" />
</SearchInput.ClearTrigger>
</SearchInput.Control>
</SearchInput.Root>
)
}Ark UI has no search input yet, so the parts come from @isbatak/ark-search-input, an Ark UI style package for React,
Solid, Vue and Svelte. It keeps the value in plain component state, with no state machine. The input shares its sizes
and variants with the input recipe. Only React has the styled component so far.
- Renders a native
<input type="search">and hides the browser's own clear button - The clear trigger only shows when there is a value, and puts focus back on the input after clearing
- Escape clears the value while the input has focus
Usage#
import { Icon } from "@isbatak/react-ui/icon"
import { SearchInput } from "@isbatak/react-ui/search-input"
export function SearchInputDemo() {
return (
<SearchInput.Root maxW="xs">
<SearchInput.Label>Search</SearchInput.Label>
<SearchInput.Control>
<SearchInput.Icon>
<Icon name="search" />
</SearchInput.Icon>
<SearchInput.Input placeholder="Search components" />
<SearchInput.ClearTrigger>
<Icon name="close" />
</SearchInput.ClearTrigger>
</SearchInput.Control>
</SearchInput.Root>
)
}<SearchInput.Root>
<SearchInput.Label />
<SearchInput.Control>
<SearchInput.Icon />
<SearchInput.Input />
<SearchInput.ClearTrigger />
</SearchInput.Control>
</SearchInput.Root>SearchInput.Icon is hidden from assistive technology, and clicking it focuses the input. The clear trigger looks like
a ghost icon button. The icons here are search and close from the active sprite, so they follow
your own icons.
Examples#
Sizes#
Set size on the root to xs, sm, md or lg. The default is md.
import { Icon } from "@isbatak/react-ui/icon"
import { SearchInput } from "@isbatak/react-ui/search-input"
import { Stack } from "styled-system/jsx"
export function SearchInputSizesDemo() {
return (
<Stack gap="5" w="full" maxW="xs">
<SearchInput.Root size="xs">
<SearchInput.Label>size xs</SearchInput.Label>
<SearchInput.Control>
<SearchInput.Icon>
<Icon name="search" />
</SearchInput.Icon>
<SearchInput.Input placeholder="Search components" />
<SearchInput.ClearTrigger>
<Icon name="close" />
</SearchInput.ClearTrigger>
</SearchInput.Control>
</SearchInput.Root>
<SearchInput.Root size="sm">
<SearchInput.Label>size sm</SearchInput.Label>
<SearchInput.Control>
<SearchInput.Icon>
<Icon name="search" />
</SearchInput.Icon>
<SearchInput.Input placeholder="Search components" />
<SearchInput.ClearTrigger>
<Icon name="close" />
</SearchInput.ClearTrigger>
</SearchInput.Control>
</SearchInput.Root>
<SearchInput.Root size="md">
<SearchInput.Label>size md</SearchInput.Label>
<SearchInput.Control>
<SearchInput.Icon>
<Icon name="search" />
</SearchInput.Icon>
<SearchInput.Input placeholder="Search components" />
<SearchInput.ClearTrigger>
<Icon name="close" />
</SearchInput.ClearTrigger>
</SearchInput.Control>
</SearchInput.Root>
<SearchInput.Root size="lg">
<SearchInput.Label>size lg</SearchInput.Label>
<SearchInput.Control>
<SearchInput.Icon>
<Icon name="search" />
</SearchInput.Icon>
<SearchInput.Input placeholder="Search components" />
<SearchInput.ClearTrigger>
<Icon name="close" />
</SearchInput.ClearTrigger>
</SearchInput.Control>
</SearchInput.Root>
</Stack>
)
}Variants#
Set variant on the root to outline, subtle or flushed. The default is outline.
import { Icon } from "@isbatak/react-ui/icon"
import { SearchInput } from "@isbatak/react-ui/search-input"
import { Stack } from "styled-system/jsx"
export function SearchInputVariantsDemo() {
return (
<Stack gap="5" w="full" maxW="xs">
<SearchInput.Root variant="outline">
<SearchInput.Label>variant outline</SearchInput.Label>
<SearchInput.Control>
<SearchInput.Icon>
<Icon name="search" />
</SearchInput.Icon>
<SearchInput.Input placeholder="Search components" />
<SearchInput.ClearTrigger>
<Icon name="close" />
</SearchInput.ClearTrigger>
</SearchInput.Control>
</SearchInput.Root>
<SearchInput.Root variant="subtle">
<SearchInput.Label>variant subtle</SearchInput.Label>
<SearchInput.Control>
<SearchInput.Icon>
<Icon name="search" />
</SearchInput.Icon>
<SearchInput.Input placeholder="Search components" />
<SearchInput.ClearTrigger>
<Icon name="close" />
</SearchInput.ClearTrigger>
</SearchInput.Control>
</SearchInput.Root>
<SearchInput.Root variant="flushed">
<SearchInput.Label>variant flushed</SearchInput.Label>
<SearchInput.Control>
<SearchInput.Icon>
<Icon name="search" />
</SearchInput.Icon>
<SearchInput.Input placeholder="Search components" />
<SearchInput.ClearTrigger>
<Icon name="close" />
</SearchInput.ClearTrigger>
</SearchInput.Control>
</SearchInput.Root>
</Stack>
)
}Default value#
Set defaultValue to start with a query.
import { Icon } from "@isbatak/react-ui/icon"
import { SearchInput } from "@isbatak/react-ui/search-input"
export function SearchInputDefaultValueDemo() {
return (
<SearchInput.Root defaultValue="button" maxW="xs">
<SearchInput.Label>Search</SearchInput.Label>
<SearchInput.Control>
<SearchInput.Icon>
<Icon name="search" />
</SearchInput.Icon>
<SearchInput.Input placeholder="Search components" />
<SearchInput.ClearTrigger>
<Icon name="close" />
</SearchInput.ClearTrigger>
</SearchInput.Control>
</SearchInput.Root>
)
}Controlled#
Pass value and onValueChange to own the query, for example to filter a list as people type.
Query: empty
import { Icon } from "@isbatak/react-ui/icon"
import { SearchInput } from "@isbatak/react-ui/search-input"
import { useState } from "react"
import { Stack, styled } from "styled-system/jsx"
export function SearchInputControlledDemo() {
const [value, setValue] = useState("")
return (
<Stack gap="3" w="full" maxW="xs">
<SearchInput.Root value={value} onValueChange={(details) => setValue(details.value)}>
<SearchInput.Label>Search</SearchInput.Label>
<SearchInput.Control>
<SearchInput.Icon>
<Icon name="search" />
</SearchInput.Icon>
<SearchInput.Input placeholder="Search components" />
<SearchInput.ClearTrigger>
<Icon name="close" />
</SearchInput.ClearTrigger>
</SearchInput.Control>
</SearchInput.Root>
<styled.p textStyle="sm" color="fg.muted">
Query: {value || "empty"}
</styled.p>
</Stack>
)
}Root provider#
Create the state with useSearchInput and pass it to SearchInput.RootProvider to control the input from outside. The
returned API has value, empty, setValue, clearValue and focus.
import { Button } from "@isbatak/react-ui/button"
import { Icon } from "@isbatak/react-ui/icon"
import { SearchInput, useSearchInput } from "@isbatak/react-ui/search-input"
import { HStack, Stack } from "styled-system/jsx"
export function SearchInputRootProviderDemo() {
const searchInput = useSearchInput()
return (
<Stack gap="3" w="full" maxW="xs" alignItems="flex-start">
<HStack gap="2">
<Button variant="outline" size="sm" onClick={() => searchInput.setValue("tooltip")}>
Search tooltip
</Button>
<Button variant="outline" size="sm" onClick={() => searchInput.clearValue()}>
Clear
</Button>
</HStack>
<SearchInput.RootProvider value={searchInput}>
<SearchInput.Label>Search</SearchInput.Label>
<SearchInput.Control>
<SearchInput.Icon>
<Icon name="search" />
</SearchInput.Icon>
<SearchInput.Input placeholder="Search components" />
<SearchInput.ClearTrigger>
<Icon name="close" />
</SearchInput.ClearTrigger>
</SearchInput.Control>
</SearchInput.RootProvider>
</Stack>
)
}Without a label#
Leave out SearchInput.Label when the field explains itself, and give the input an aria-label instead.
import { Icon } from "@isbatak/react-ui/icon"
import { SearchInput } from "@isbatak/react-ui/search-input"
export function SearchInputWithoutLabelDemo() {
return (
<SearchInput.Root maxW="xs">
<SearchInput.Control>
<SearchInput.Icon>
<Icon name="search" />
</SearchInput.Icon>
<SearchInput.Input placeholder="Search components" aria-label="Search components" />
<SearchInput.ClearTrigger>
<Icon name="close" />
</SearchInput.ClearTrigger>
</SearchInput.Control>
</SearchInput.Root>
)
}Without an icon#
Leave out SearchInput.Icon and the input drops the space it reserves at its start.
import { Icon } from "@isbatak/react-ui/icon"
import { SearchInput } from "@isbatak/react-ui/search-input"
export function SearchInputWithoutIconDemo() {
return (
<SearchInput.Root maxW="xs">
<SearchInput.Label>Search</SearchInput.Label>
<SearchInput.Control>
<SearchInput.Icon>
<Icon name="search" />
</SearchInput.Icon>
<SearchInput.Input placeholder="Search components" />
<SearchInput.ClearTrigger>
<Icon name="close" />
</SearchInput.ClearTrigger>
</SearchInput.Control>
</SearchInput.Root>
)
}Disabled#
Set disabled on the root to disable the input and the clear trigger. readOnly keeps the value selectable and hides
the clear trigger.
import { Icon } from "@isbatak/react-ui/icon"
import { SearchInput } from "@isbatak/react-ui/search-input"
export function SearchInputDisabledDemo() {
return (
<SearchInput.Root disabled defaultValue="button" maxW="xs">
<SearchInput.Label>Search</SearchInput.Label>
<SearchInput.Control>
<SearchInput.Icon>
<Icon name="search" />
</SearchInput.Icon>
<SearchInput.Input placeholder="Search components" />
<SearchInput.ClearTrigger>
<Icon name="close" />
</SearchInput.ClearTrigger>
</SearchInput.Control>
</SearchInput.Root>
)
}Invalid#
Set invalid to give the input the error border. Add the error text below it, here with the field recipe's
errorText. required, name and form go on the root too.
import { field } from "@isbatak/panda-ds/recipes"
import { Icon } from "@isbatak/react-ui/icon"
import { SearchInput } from "@isbatak/react-ui/search-input"
export function SearchInputInvalidDemo() {
const fieldClasses = field()
return (
<SearchInput.Root invalid defaultValue="a" maxW="xs">
<SearchInput.Label>Search</SearchInput.Label>
<SearchInput.Control>
<SearchInput.Icon>
<Icon name="search" />
</SearchInput.Icon>
<SearchInput.Input placeholder="Search components" />
<SearchInput.ClearTrigger>
<Icon name="close" />
</SearchInput.ClearTrigger>
</SearchInput.Control>
<span className={fieldClasses.errorText}>Type at least 2 characters.</span>
</SearchInput.Root>
)
}Translations#
The clear trigger's label is "Clear search". Pass translations.clearTrigger to change it.
import { Icon } from "@isbatak/react-ui/icon"
import { SearchInput } from "@isbatak/react-ui/search-input"
export function SearchInputTranslationsDemo() {
return (
<SearchInput.Root translations={{ clearTrigger: "Suche löschen" }} defaultValue="Schaltfläche" maxW="xs">
<SearchInput.Label>Suche</SearchInput.Label>
<SearchInput.Control>
<SearchInput.Icon>
<Icon name="search" />
</SearchInput.Icon>
<SearchInput.Input placeholder="Komponenten durchsuchen" />
<SearchInput.ClearTrigger>
<Icon name="close" />
</SearchInput.ClearTrigger>
</SearchInput.Control>
</SearchInput.Root>
)
}Recipe#
| Variant | Values | Default |
|---|---|---|
variant | outline, subtle, flushed | outline |
size | xs, sm, md, lg | md |
| Variable | Description |
|---|---|
--input-height | The input height, set by size. The icon area and the space for the clear trigger |
--focus-color | The focus ring color, colors.colorPalette.focusRing |
--error-color | The border and focus ring color when invalid, colors.border.error |