Skip to main content

Search input field with clear button and search icon

Import

import { SearchField } from "heroui-solid";

Usage

import { Label, SearchField } from "heroui-solid"

export function Basic() {
  return (
    <SearchField name="search">
      <Label>Search</Label>
      <SearchField.Group>
        <SearchField.SearchIcon />
        <SearchField.Input class="w-[280px]" placeholder="Search..." />
        <SearchField.ClearButton />
      </SearchField.Group>
    </SearchField>
  )
}

Anatomy

import { SearchField, Label, Description, FieldError } from "heroui-solid";

export default () => (
  <SearchField>
    <Label />
    <SearchField.Group>
      <SearchField.SearchIcon />
      <SearchField.Input />
      <SearchField.ClearButton />
    </SearchField.Group>
    <Description />
    <FieldError />
  </SearchField>
);

SearchField allows users to enter and clear a search query. It includes a search icon and an optional clear button for easy reset.

With Description

Enter keywords to search for products
Search by name, email, or username
import { Description, Label, SearchField } from "heroui-solid"

export function WithDescription() {
  return (
    <div class="flex flex-col gap-4">
      <SearchField name="search">
        <Label>Search products</Label>
        <SearchField.Group>
          <SearchField.SearchIcon />
          <SearchField.Input
            class="w-[280px]"
            placeholder="Search products..."
          />
          <SearchField.ClearButton />
        </SearchField.Group>
        <Description>Enter keywords to search for products</Description>
      </SearchField>
      <SearchField name="search-users">
        <Label>Search users</Label>
        <SearchField.Group>
          <SearchField.SearchIcon />
          <SearchField.Input class="w-[280px]" placeholder="Search users..." />
          <SearchField.ClearButton />
        </SearchField.Group>
        <Description>Search by name, email, or username</Description>
      </SearchField>
    </div>
  )
}

Required Field

Minimum 3 characters required
import { Description, Label, SearchField } from "heroui-solid"

export function Required() {
  return (
    <div class="flex flex-col gap-4">
      <SearchField isRequired name="search">
        <Label>Search</Label>
        <SearchField.Group>
          <SearchField.SearchIcon />
          <SearchField.Input class="w-[280px]" placeholder="Search..." />
          <SearchField.ClearButton />
        </SearchField.Group>
      </SearchField>
      <SearchField isRequired name="search-query">
        <Label>Search query</Label>
        <SearchField.Group>
          <SearchField.SearchIcon />
          <SearchField.Input
            class="w-[280px]"
            placeholder="Enter search query..."
          />
          <SearchField.ClearButton />
        </SearchField.Group>
        <Description>Minimum 3 characters required</Description>
      </SearchField>
    </div>
  )
}

Validation

Use isInvalid together with FieldError to surface validation messages.

Search query must be at least 3 characters
Invalid characters in search query
import { FieldError, Label, SearchField } from "heroui-solid"

export function Validation() {
  return (
    <div class="flex flex-col gap-4">
      <SearchField isInvalid isRequired name="search" value="ab">
        <Label>Search</Label>
        <SearchField.Group>
          <SearchField.SearchIcon />
          <SearchField.Input class="w-[280px]" placeholder="Search..." />
          <SearchField.ClearButton />
        </SearchField.Group>
        <FieldError>Search query must be at least 3 characters</FieldError>
      </SearchField>
      <SearchField isInvalid name="search-invalid">
        <Label>Search</Label>
        <SearchField.Group>
          <SearchField.SearchIcon />
          <SearchField.Input
            class="w-[280px]"
            placeholder="Search..."
            value="invalid@query"
          />
          <SearchField.ClearButton />
        </SearchField.Group>
        <FieldError>Invalid characters in search query</FieldError>
      </SearchField>
    </div>
  )
}

Disabled State

This search field is disabled
This search field is disabled
import { Description, Label, SearchField } from "heroui-solid"

export function Disabled() {
  return (
    <div class="flex flex-col gap-4">
      <SearchField isDisabled name="search" value="Disabled search">
        <Label>Search</Label>
        <SearchField.Group>
          <SearchField.SearchIcon />
          <SearchField.Input class="w-[280px]" placeholder="Search..." />
          <SearchField.ClearButton />
        </SearchField.Group>
        <Description>This search field is disabled</Description>
      </SearchField>
      <SearchField isDisabled name="search-empty">
        <Label>Search</Label>
        <SearchField.Group>
          <SearchField.SearchIcon />
          <SearchField.Input class="w-[280px]" placeholder="Search..." />
          <SearchField.ClearButton />
        </SearchField.Group>
        <Description>This search field is disabled</Description>
      </SearchField>
    </div>
  )
}

Controlled

Control the value to synchronize with other components or perform custom formatting.

Current value: (empty)
import { Button, Description, Label, SearchField } from "heroui-solid"
import { createSignal } from "solid-js"

export function Controlled() {
  const [value, setValue] = createSignal("")

  return (
    <div class="flex flex-col gap-4">
      <SearchField name="search" value={value()} onChange={setValue}>
        <Label>Search</Label>
        <SearchField.Group>
          <SearchField.SearchIcon />
          <SearchField.Input class="w-[280px]" placeholder="Search..." />
          <SearchField.ClearButton />
        </SearchField.Group>
        <Description>Current value: {value() || "(empty)"}</Description>
      </SearchField>
      <div class="flex gap-2">
        <Button variant="tertiary" onClick={() => setValue("")}>
          Clear
        </Button>
        <Button variant="tertiary" onClick={() => setValue("example query")}>
          Set example
        </Button>
      </div>
    </div>
  )
}

With Validation

Implement custom validation logic with controlled values.

Enter at least 3 characters to search
import { Description, FieldError, Label, SearchField } from "heroui-solid"
import { createSignal, Show } from "solid-js"

export function WithValidation() {
  const [value, setValue] = createSignal("")
  const isInvalid = () => value().length > 0 && value().length < 3

  return (
    <div class="flex flex-col gap-4">
      <SearchField
        isRequired
        isInvalid={isInvalid()}
        name="search"
        value={value()}
        onChange={setValue}
      >
        <Label>Search</Label>
        <SearchField.Group>
          <SearchField.SearchIcon />
          <SearchField.Input class="w-[280px]" placeholder="Search..." />
          <SearchField.ClearButton />
        </SearchField.Group>
        <Show
          when={isInvalid()}
          fallback={
            <Description>Enter at least 3 characters to search</Description>
          }
        >
          <FieldError>Search query must be at least 3 characters</FieldError>
        </Show>
      </SearchField>
    </div>
  )
}

Custom Icons

Customize the search icon and clear button icons.

Custom icon children
import { Description, Label, SearchField } from "heroui-solid"

export function CustomIcons() {
  return (
    <div class="flex flex-col gap-4">
      <SearchField name="search-custom">
        <Label>Search (Custom Icons)</Label>
        <SearchField.Group>
          <SearchField.SearchIcon>
            {/* biome-ignore lint/a11y/noSvgWithoutTitle: decorative icon, mirrors upstream demo */}
            <svg
              height="16"
              viewBox="0 0 16 16"
              width="16"
              xmlns="http://www.w3.org/2000/svg"
            >
              <path
                clip-rule="evenodd"
                d="M12.5 4c0 .174-.071.513-.885.888S9.538 5.5 8 5.5s-2.799-.237-3.615-.612C3.57 4.513 3.5 4.174 3.5 4s.071-.513.885-.888S6.462 2.5 8 2.5s2.799.237 3.615.612c.814.375.885.714.885.888m-1.448 2.66C10.158 6.888 9.115 7 8 7s-2.158-.113-3.052-.34l1.98 2.905c.21.308.322.672.322 1.044v3.37q.088.02.25.021c.422 0 .749-.14.95-.316c.185-.162.3-.38.3-.684v-2.39c0-.373.112-.737.322-1.045zM8 1c3.314 0 6 1 6 3a3.24 3.24 0 0 1-.563 1.826l-3.125 4.584a.35.35 0 0 0-.062.2V13c0 1.5-1.25 2.5-2.75 2.5s-1.75-1-1.75-1v-3.89a.35.35 0 0 0-.061-.2L2.563 5.826A3.24 3.24 0 0 1 2 4c0-2 2.686-3 6-3m-.88 12.936q-.015-.008-.013-.01z"
                fill="currentColor"
                fill-rule="evenodd"
              />
            </svg>
          </SearchField.SearchIcon>
          <SearchField.Input class="w-[280px]" placeholder="Search..." />
          <SearchField.ClearButton>
            {/* biome-ignore lint/a11y/noSvgWithoutTitle: decorative icon, mirrors upstream demo */}
            <svg
              height="16"
              viewBox="0 0 16 16"
              width="16"
              xmlns="http://www.w3.org/2000/svg"
            >
              <path
                clip-rule="evenodd"
                d="M8 15A7 7 0 1 0 8 1a7 7 0 0 0 0 14M6.53 5.47a.75.75 0 0 0-1.06 1.06L6.94 8L5.47 9.47a.75.75 0 1 0 1.06 1.06L8 9.06l1.47 1.47a.75.75 0 1 0 1.06-1.06L9.06 8l1.47-1.47a.75.75 0 1 0-1.06-1.06L8 6.94z"
                fill="currentColor"
                fill-rule="evenodd"
              />
            </svg>
          </SearchField.ClearButton>
        </SearchField.Group>
        <Description>Custom icon children</Description>
      </SearchField>
    </div>
  )
}

Full Width

import { Label, SearchField } from "heroui-solid"

export function FullWidth() {
  return (
    <div class="w-[400px] space-y-4">
      <SearchField fullWidth name="search">
        <Label>Search</Label>
        <SearchField.Group>
          <SearchField.SearchIcon />
          <SearchField.Input placeholder="Search..." />
          <SearchField.ClearButton />
        </SearchField.Group>
      </SearchField>
    </div>
  )
}

Variants

The SearchField component supports two visual variants:

  • primary (default) - Standard styling with shadow, suitable for most use cases
  • secondary - Lower emphasis variant without shadow, suitable for use in Surface components
import { Label, SearchField } from "heroui-solid"

export function Variants() {
  return (
    <div class="flex flex-col gap-4">
      <SearchField name="primary-search" variant="primary">
        <Label>Primary variant</Label>
        <SearchField.Group>
          <SearchField.SearchIcon />
          <SearchField.Input class="w-[280px]" placeholder="Search..." />
          <SearchField.ClearButton />
        </SearchField.Group>
      </SearchField>
      <SearchField name="secondary-search" variant="secondary">
        <Label>Secondary variant</Label>
        <SearchField.Group>
          <SearchField.SearchIcon />
          <SearchField.Input class="w-[280px]" placeholder="Search..." />
          <SearchField.ClearButton />
        </SearchField.Group>
      </SearchField>
    </div>
  )
}

In Surface

When used inside a Surface component, use variant="secondary" to apply the lower emphasis variant suitable for surface backgrounds.

Enter keywords to search
Use filters to refine your search
import { Description, Label, SearchField, Surface } from "heroui-solid"

export function OnSurface() {
  return (
    <Surface class="flex w-full max-w-sm flex-col gap-4 rounded-3xl p-6">
      <SearchField name="search" variant="secondary">
        <Label>Search</Label>
        <SearchField.Group>
          <SearchField.SearchIcon />
          <SearchField.Input class="w-full" placeholder="Search..." />
          <SearchField.ClearButton />
        </SearchField.Group>
        <Description>Enter keywords to search</Description>
      </SearchField>
      <SearchField name="search-2" variant="secondary">
        <Label>Advanced search</Label>
        <SearchField.Group>
          <SearchField.SearchIcon />
          <SearchField.Input class="w-full" placeholder="Advanced search..." />
          <SearchField.ClearButton />
        </SearchField.Group>
        <Description>Use filters to refine your search</Description>
      </SearchField>
    </Surface>
  )
}

Form Example

Complete form integration with validation and submission handling.

Enter at least 3 characters to search
import {
  Button,
  Description,
  FieldError,
  Form,
  Label,
  SearchField,
  Spinner
} from "heroui-solid"
import { createSignal, Show } from "solid-js"

export function FormExample() {
  const [value, setValue] = createSignal("")
  const [isSubmitting, setIsSubmitting] = createSignal(false)
  const MIN_LENGTH = 3
  const isInvalid = () => value().length > 0 && value().length < MIN_LENGTH

  const handleSubmit = (e: SubmitEvent) => {
    e.preventDefault()

    if (value().length < MIN_LENGTH) {
      return
    }

    setIsSubmitting(true)

    // Simulate API call
    setTimeout(() => {
      console.log("Search submitted:", { query: value() })
      setValue("")
      setIsSubmitting(false)
    }, 1500)
  }

  return (
    <Form class="flex w-[280px] flex-col gap-4" onSubmit={handleSubmit}>
      <SearchField
        isRequired
        isInvalid={isInvalid()}
        name="search"
        value={value()}
        onChange={setValue}
      >
        <Label>Search products</Label>
        <SearchField.Group>
          <SearchField.SearchIcon />
          <SearchField.Input class="w-full" placeholder="Search products..." />
          <SearchField.ClearButton />
        </SearchField.Group>
        <Show
          when={isInvalid()}
          fallback={
            <Description>
              Enter at least {MIN_LENGTH} characters to search
            </Description>
          }
        >
          <FieldError>
            Search query must be at least {MIN_LENGTH} characters
          </FieldError>
        </Show>
      </SearchField>
      <Button
        class="w-full"
        isDisabled={value().length < MIN_LENGTH}
        isPending={isSubmitting()}
        type="submit"
        variant="primary"
      >
        <Show when={isSubmitting()} fallback="Search">
          <Spinner color="current" size="sm" />
          Searching...
        </Show>
      </Button>
    </Form>
  )
}

With Keyboard Shortcut

Add keyboard shortcuts to quickly focus the search field.

Use keyboard shortcut to quickly focus this field
PressSto focus the search field
import { Description, Kbd, Label, SearchField } from "heroui-solid"
import { createSignal, onCleanup, onMount } from "solid-js"

export function WithKeyboardShortcut() {
  let inputRef: HTMLInputElement | undefined
  const [value, setValue] = createSignal("")

  onMount(() => {
    const handleKeyDown = (e: KeyboardEvent) => {
      // Check for Shift+S
      if (
        e.shiftKey &&
        e.key === "S" &&
        !e.metaKey &&
        !e.ctrlKey &&
        !e.altKey
      ) {
        e.preventDefault()
        inputRef?.focus()
      }
      // Check for ESC key to blur the input
      if (e.key === "Escape" && document.activeElement === inputRef) {
        inputRef?.blur()
      }
    }

    window.addEventListener("keydown", handleKeyDown)
    onCleanup(() => window.removeEventListener("keydown", handleKeyDown))
  })

  return (
    <div class="flex flex-col gap-4">
      <div>
        <SearchField name="search" value={value()} onChange={setValue}>
          <Label>Search</Label>
          <SearchField.Group>
            <SearchField.SearchIcon />
            <SearchField.Input
              ref={inputRef}
              class="w-[280px]"
              placeholder="Search..."
            />
            <SearchField.ClearButton />
          </SearchField.Group>
          <Description>
            Use keyboard shortcut to quickly focus this field
          </Description>
        </SearchField>
      </div>
      <div class="text-default-500 flex items-center gap-2 text-sm">
        <span>Press</span>
        <Kbd>
          <Kbd.Abbr keyValue="shift" />
          <Kbd.Content>S</Kbd.Content>
        </Kbd>
        <span>to focus the search field</span>
      </div>
    </div>
  )
}

Styling

Passing Tailwind CSS classes

import { SearchField, Label } from "heroui-solid";

function CustomSearchField() {
  return (
    <SearchField class="gap-2">
      <Label class="text-sm font-semibold">Search</Label>
      <SearchField.Group class="rounded-xl border-2">
        <SearchField.SearchIcon class="text-blue-500" />
        <SearchField.Input class="text-center font-bold" />
        <SearchField.ClearButton class="text-red-500" />
      </SearchField.Group>
    </SearchField>
  );
}

Customizing the component classes

SearchField uses CSS classes that can be customized. Override the component classes to match your design system.

@layer components {
  .search-field {
    @apply flex flex-col gap-1;
  }

  .search-field__group {
    @apply bg-field text-field-foreground shadow-field rounded-field inline-flex h-9 items-center overflow-hidden border;
  }

  .search-field__input {
    @apply flex-1 rounded-none border-0 bg-transparent px-3 py-2 shadow-none outline-none;
  }

  .search-field__search-icon {
    @apply text-field-placeholder pointer-events-none ml-3 mr-0 size-4 shrink-0;
  }

  .search-field__clear-button {
    @apply mr-1 shrink-0;
  }
}

CSS Classes

  • .search-field – Root container with minimal styling (flex flex-col gap-1)
  • .search-field__group – Container for search icon, input, and clear button with border and background styling
  • .search-field__input – The search input field
  • .search-field__search-icon – The search icon displayed on the left
  • .search-field__clear-button – Button to clear the search field
  • .search-field--primary – Primary variant with shadow (default)
  • .search-field--secondary – Secondary variant without shadow, suitable for use in surfaces

Interactive States

SearchField manages these data attributes based on its state:

  • Invalid: [data-invalid="true"] – Automatically hides the description slot when invalid
  • Disabled: [data-disabled="true"] – Applied when isDisabled is true
  • Focus Within: :focus-within – Applied when the input is focused
  • Hovered: :hover – Applied when hovering over the group
  • Empty: [data-empty="true"] – Applied when the field is empty (hides clear button)

API Reference

SearchField Props

Base Props

PropTypeDefaultDescription
classstring-Tailwind classes merged with the component styles.
childrenJSX.Element-Child components (Label, Group, Input, etc.).
fullWidthbooleanfalseWhether the search field should take full width of its container.
variant"primary" | "secondary""primary"Visual variant of the component.

Value Props

PropTypeDefaultDescription
valuestring-Current value (controlled).
defaultValuestring-Default value (uncontrolled).
onChange(value: string) => void-Handler called when the value changes.

Validation Props

PropTypeDefaultDescription
isRequiredbooleanfalseWhether user input is required.
isInvalidboolean-Whether the value is invalid.

State Props

PropTypeDefaultDescription
isDisabledboolean-Whether the input is disabled.
isReadOnlyboolean-Whether the input can be selected but not changed.

Form Props

PropTypeDefaultDescription
namestring-Name of the input element, for HTML form submission.
autoFocusboolean-Whether the element should receive focus on render.

Event Props

PropTypeDefaultDescription
onSubmit(value: string) => void-Handler called when the user submits (Enter key).
onClear() => void-Handler called when the clear button is pressed.

Accessibility Props

PropTypeDefaultDescription
aria-labelstring-Accessibility label when no visible label is present.
aria-labelledbystring-ID of elements that label this field.
aria-describedbystring-ID of elements that describe this field.

Composition Components

SearchField works with these separate components that should be imported and used directly:

  • SearchField.Group – Container for search icon, input, and clear button
  • SearchField.Input – The search input field
  • SearchField.SearchIcon – The search icon displayed on the left
  • SearchField.ClearButton – Button to clear the search field
  • Label – Field label component from heroui-solid
  • Description – Helper text component from heroui-solid
  • FieldError – Validation error message from heroui-solid

SearchField.Group Props

PropTypeDefaultDescription
classstring-CSS classes for styling.
childrenJSX.Element-Child components (SearchIcon, Input, ClearButton).

SearchField.Input Props

PropTypeDefaultDescription
classstring-CSS classes for styling.
placeholderstring-Placeholder text displayed when the input is empty.
typestring"search"Input type (automatically set to "search").

SearchField.SearchIcon Props

PropTypeDefaultDescription
childrenJSX.Element<IconSearch />Custom icon element. Defaults to search icon.
classstring-CSS classes for styling.

SearchField.ClearButton Props

PropTypeDefaultDescription
childrenJSX.Element-Icon or content for the button. Defaults to close icon.
classstring-CSS classes for styling.

Differences from HeroUI React

  • Use class instead of className.
  • The clear button and Escape key clear the value; onSubmit(value) fires on Enter. Built on Kobalte's TextField primitive rather than React Aria's SearchField.
  • No render-prop (children/className as functions) API — compose with child components instead.

Last updated: 7/19/26, 3:27 AM

HeroUI SolidUnofficial SolidJS port of HeroUI v3, built on Kobalte and @heroui/styles