npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

@haus-storefront-react/search

v0.0.50

Published

A headless, flexible search component for e-commerce storefronts. Supports real-time search, product and collection results, price display, and more. Built with TypeScript, accessibility-first design, and platform-agnostic architecture.

Downloads

485

Readme

Search Component

A headless, flexible search component for e-commerce storefronts. Supports real-time search, product and collection results, price display, and more. Built with TypeScript, accessibility-first design, and platform-agnostic architecture.

Installation

npm install @haus-storefront-react/search
# or
yarn add @haus-storefront-react/search

Usage Example

import { Price } from '@haus-storefront-react/common-ui'
import { Search } from '@haus-storefront-react/search'
import { Link } from '@tanstack/react-router'
;<Search.Root term="" take={5}>
  {(searchContext) => (
    <div>
      <Search.Input placeholder="Search for products..." />
      <Search.Clear>✕</Search.Clear>

      <Search.Loading>
        <div>Loading search results...</div>
      </Search.Loading>

      <Search.Empty>
        <div>No results found. Try a different search term.</div>
      </Search.Empty>

      <Search.Results>
        {({ isLoading, collections, items }) => {
          if (isLoading) return <div>Loading...</div>
          if (!items || items.length === 0) return <div>No results found</div>

          return (
            <div>
              {/* Collections */}
              {collections?.map((collection) => (
                <Search.CollectionItem key={collection.id} collection={collection}>
                  <Search.CollectionImage alt={collection.name} />
                  <div>{collection.name}</div>
                </Search.CollectionItem>
              ))}

              {/* Products */}
              {items?.map((product) => (
                <Search.ProductItem key={product.productId} product={product}>
                  <Link to="/product/$productId" params={{ productId: product.productId }}>
                    <Search.ProductImage alt={product.productName} />
                    <div>{product.productName}</div>
                    <Search.Price>
                      {({ price, priceWithTax, currencyCode }) => (
                        <Price.Root
                          price={price}
                          priceWithTax={priceWithTax}
                          currencyCode={currencyCode}
                          asChild
                        >
                          <div>
                            <Price.Amount withCurrency />
                            <Price.Currency />
                          </div>
                        </Price.Root>
                      )}
                    </Search.Price>
                  </Link>
                </Search.ProductItem>
              ))}
            </div>
          )
        }}
      </Search.Results>
    </div>
  )}
</Search.Root>

Features

  • 🔍 Real-time search with debounced API calls
  • 🛒 Product and collection search with unified results
  • ♿ Accessibility-first, platform-agnostic
  • 💸 Price display with currency support
  • 🖼️ Image handling for products and collections
  • 🎨 Headless, fully customizable
  • ⚡ TypeScript support
  • 🔄 Loading and error states built-in

API Reference

<Search.Root>

Context provider for search functionality.

Props:

  • term: string – Initial search term
  • take?: number – Number of results to fetch (default: 3)
  • groupByProduct?: boolean – Group results by product (default: true)
  • children: (context) => ReactNode – Render prop with search context

<Search.Input>

Search input field. Accepts all <input> props

<Search.Clear>

Clear button that appears when there's a search term. Accepts all <button> props.

<Search.Results>

Container for search results. Render prop receives:

  • isLoading: boolean – Loading state
  • collections?: Collection[] – Collection results
  • items?: SearchResult[] – Product results
  • totalItems?: number – Total number of products matching input
  • facets?: Facet[] – Facet data

<Search.ProductItem>

Wraps a single product. Must be used inside Search.Results.

  • product: SearchResult – The product object

<Search.ProductImage>

Displays the product image. Accepts all <img> props.

<Search.CollectionItem>

Wraps a single collection. Must be used inside Search.Results.

  • collection: Collection – The collection object

<Search.CollectionImage>

Displays the collection image. Accepts all <img> props.

<Search.Price>

Render prop for price info:

  • price, priceWithTax, currencyCode, isFromPrice

<Search.Loading>

Component that shows during loading states. Accepts all <div> props.

<Search.Empty>

Component that shows when no results are found. Accepts all <div> props.

Search Context

The search context provides the following:

interface SearchContextValue {
  data: UseSearchFieldResponse | null
  isLoading: boolean
  error: Error | null
  variables: SearchVariables
  setVariables: (variables: SearchVariables) => void
  search: (term: string) => void
  clear: () => void
}

Integration with ProductList

For full search results with infinite pagination, use the ProductList component:

import { ProductList } from '@haus-storefront-react/product-list'

function SearchResultsPage() {
  const [searchParams] = useSearchParams()
  const searchTerm = searchParams.get('term') || ''

  return (
    <ProductList.Root
      searchInputProps={{
        term: searchTerm,
        take: 20,
        groupByProduct: true,
      }}
      infinitePagination={true}
    >
      {({ products, isLoading, error, totalItems }) => {
        if (isLoading) return <div>Loading products...</div>
        if (error) return <div>Error loading products</div>
        if (!products.length) return <div>No products found</div>

        return (
          <>
            <div>
              Found {totalItems} products for "{searchTerm}"
            </div>
            <div
              style={{
                display: 'grid',
                gridTemplateColumns: 'repeat(auto-fill, minmax(250px, 1fr))',
              }}
            >
              {products.map((product) => (
                <ProductList.Item key={product.productId} product={product}>
                  <ProductList.Image alt={product.productName} />
                  <h3>{product.productName}</h3>
                  <ProductList.Price>
                    {({ price, currencyCode }) => (
                      <span>
                        {price} {currencyCode}
                      </span>
                    )}
                  </ProductList.Price>
                </ProductList.Item>
              ))}
            </div>
            <ProductList.Pagination.Root>
              {({ canGoForward, nextPage }) => (
                <button onClick={nextPage} disabled={!canGoForward}>
                  Load More Products
                </button>
              )}
            </ProductList.Pagination.Root>
          </>
        )
      }}
    </ProductList.Root>
  )
}

Navigation Integration

For search with navigation to full results page:

import { Search } from '@haus-storefront-react/search'
import { useNavigate } from '@tanstack/react-router'

function SearchWithNavigation() {
  const navigate = useNavigate()

  return (
    <Search.Root term="" take={5}>
      {(searchContext) => (
        <div>
          <Search.Input
            placeholder="Search for products..."
            onKeyUp={(e) => {
              if (e.key === 'Enter') {
                const searchTerm = e.currentTarget.value
                searchContext.search(searchTerm)
                navigate({ to: '/search', search: { term: searchTerm } })
              }
            }}
          />

          <Search.Results>
            {({ isLoading, collections, items }) => (
              <div>
                {/* Show first collection only */}
                {collections?.[0] && (
                  <Search.CollectionItem collection={collections[0]}>
                    <Search.CollectionImage alt={collections[0].name} />
                    <h4>{collections[0].name}</h4>
                  </Search.CollectionItem>
                )}

                {/* Products with navigation */}
                {items?.map((product) => (
                  <Search.ProductItem key={product.productId} product={product}>
                    <Link to="/product/$productId" params={{ productId: product.productId }}>
                      <Search.ProductImage alt={product.productName} />
                      <h4>{product.productName}</h4>
                      <Search.Price>
                        {({ price, currencyCode }) => (
                          <span>
                            {price} {currencyCode}
                          </span>
                        )}
                      </Search.Price>
                    </Link>
                  </Search.ProductItem>
                ))}

                {/* Show All Results Button */}
                <button
                  onClick={() => {
                    const searchTerm = searchContext.variables.term
                    if (searchTerm) {
                      navigate({ to: '/search', search: { term: searchTerm } })
                    }
                  }}
                >
                  Show All Results
                </button>
              </div>
            )}
          </Search.Results>
        </div>
      )}
    </Search.Root>
  )
}

Error Handling

The search component handles errors gracefully:

  • Network errors are caught and displayed
  • Invalid search terms are handled
  • Loading states prevent multiple simultaneous requests
  • Empty states provide clear feedback

Integration

This component integrates seamlessly with other components in the ecosystem:

  • ProductList – For full search results with pagination
  • Price – For consistent price display
  • Navigation – For routing to product and search pages
  • Cart components – For add-to-cart functionality

See the source code and TypeScript types for full details and advanced usage.