VisorVisor
Patterns

Search Results

A search UI combining a type-ahead input, command palette, and filterable result cards with empty-state fallback.

Preview

Getting Started with Visor
/docs/getting-started

Learn how to install and configure Visor in your Next.js project in under five minutes.

Button Component
/docs/components/button

The Button component supports multiple variants including primary, outline, ghost, and destructive.

Theme Architecture
/docs/tokens/themes

Visor's three-tier token architecture separates primitives, semantic tokens, and adaptive theme values.

Design Token Rules
/docs/tokens/rules

Token rules enforce consistent spacing, shadow, motion, and color usage across all components.

When to Use

  • Search pages where users need to filter and browse a large dataset
  • When combining free-text search with faceted category filters
  • Directory, catalog, or discovery interfaces with card-based result display

Components Used

Structure

<div className="search-layout">
  <div className="search-controls">
    <SearchInput
      value={query}
      onChange={setQuery}
      placeholder="Search items..."
      onClear={() => setQuery("")}
    />
    <Combobox
      options={categoryOptions}
      value={selectedCategory}
      onValueChange={setSelectedCategory}
      placeholder="All categories"
    />
  </div>

  <Command shouldFilter={false}>
    <CommandList>
      {isLoading && (
        <CommandEmpty>Searching...</CommandEmpty>
      )}
      {!isLoading && results.length === 0 && (
        <EmptyState
          icon={<MagnifyingGlassIcon />}
          title="No results found"
          description={`No items match "${query}". Try a different search term.`}
        />
      )}
      {!isLoading && results.length > 0 && (
        <CommandGroup heading={`${results.length} results`}>
          {results.map((item) => (
            <CommandItem key={item.id} onSelect={() => navigate(item.href)}>
              <Card className="search-result-card">
                <CardHeader>
                  <CardTitle>{item.title}</CardTitle>
                  <Badge variant="secondary">{item.category}</Badge>
                </CardHeader>
                <CardContent>
                  <p>{item.description}</p>
                </CardContent>
              </Card>
            </CommandItem>
          ))}
        </CommandGroup>
      )}
    </CommandList>
  </Command>
</div>

Notes

  • Pass shouldFilter={false} to Command when search is server-side or debounced.
  • SearchInput provides the clear button and search icon affordance; avoid reimplementing in plain Input.
  • EmptyState handles the zero-results case — do not use a bare paragraph.
  • Combobox for category filter is preferred over Select when the option list may be long or searchable.