shadcn.io is an independent project built on shadcn/ui
Search Products has a query, a sort that starts on Relevance, and a min and max price. Categories are Electronics, Clothing, Books, and Home & Garden. In Stock Only starts unchecked. Reset clears them.
Use it beside a product list. A keyword with no filters is a single search Input. In React, Base UI does not apply the filters. Submit hands them over.
Installation
Usage
<form className="w-full max-w-md" onSubmit={form.handleSubmit(onSubmit)}>
<FieldGroup>
<div className="flex flex-col">
<h2 className="font-bold text-2xl">Search Products</h2>
<p className="text-muted-foreground text-sm">
Filter and find what you're looking for
</p>
</div>
<Controller
control={form.control}
name="query"
render={({ field, fieldState }) => (
<Field invalid={fieldState.invalid}>
<FieldLabel htmlFor="search-filter-form-query">Search</FieldLabel>
<Input
{...field}
id="search-filter-form-query"
placeholder="Search products..."
type="search"
/>
<FieldError>{fieldState.error?.message}</FieldError>
</Field>
)}
/>
<Controller
control={form.control}
name="sortBy"
render={({ field, fieldState }) => (
<Field invalid={fieldState.invalid}>
<FieldLabel htmlFor="search-filter-form-sortBy">
{/* The rest of the file is under Installation. */}FAQ
Does the search box have to contain text?
No. query is a plain z.string() starting as "", so an empty search submits. The Input has type="search" and the placeholder "Search products...".
How does Sort By get its starting value?
From defaultValues: sortBy is relevance, so the trigger shows Relevance. The other options are Price: Low to High, Price: High to Low and Newest First.
What do blank min and max prices become?
undefined. Both are z.number().min(0).optional(), and each number Input passes undefined to field.onChange when empty and a number otherwise. A negative price fails with Zod's default message.
How does a category checkbox update the array?
Each box is checked when field.value includes its id. Checking it adds the id to a new array and unchecking filters it out, then field.onChange stores the result. The handler is onCheckedChange.
API Reference
Field
Sets invalid={fieldState.invalid}, htmlFor="search-filter-form-query", htmlFor="search-filter-form-sortBy", variant="label", htmlFor="search-filter-form-priceMin" and htmlFor="search-filter-form-priceMax". The rest is on the Field page.
Input
Sets field, id="search-filter-form-query", placeholder="Search products...", type="search", id="search-filter-form-priceMin" and name={field.name}. The rest is on the Input page.
Select
Sets items={sortOptions}, name={field.name}, onValueChange, next, id="search-filter-form-sortBy" and value={item.value}. The rest is on the Select page.
Checkbox
Sets checked={field.value.includes(category.id)}, id, search-filter-form-, category, id and name={field.name}. The rest is on the Checkbox page.
Button
Sets type="submit" and onClick. The rest is on the Button page.





