> ## Documentation Index
> Fetch the complete documentation index at: https://docs.trychannel3.com/llms.txt
> Use this file to discover all available pages before exploring further.

# UI + Hooks

We've built UI components and hooks so you can add shopping experiences to your app without starting from scratch.

The library is open source and installs through the [shadcn registry](https://ui.shadcn.com/registry). That means each component lands in your repo as regular source code you can edit. Two ready-made blocks cover most apps. If you need more control, you can use the smaller building blocks or the headless hooks below.

<Card title="channel3-ai/channel3-ui" icon="github" href="https://github.com/channel3-ai/channel3-ui" arrow="true">
  Full catalog, install commands, and contributor guide on GitHub.
</Card>

## Install

```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
npx shadcn@latest add https://ui.trychannel3.com/r/all.json
```

This copies all components into your `components/` folder. They work directly with the Channel3 SDK — pass in a `ProductDetail` from search or a product fetch with no extra mapping step.

## Blocks

Two all-in-one blocks cover most shopping UIs:

### `product-search`

A full search page in one component: search bar, filters (brand, category, color, price), and a results grid that loads more as you scroll.

```tsx theme={"theme":{"light":"github-light","dark":"github-dark"}}
import { ProductSearch } from "@/components/channel3/product-search";

export default function ShopPage() {
  return (
    <ProductSearch
      onSearch={async (params) => {
        "use server";
        return channel3Client.products.search(params);
      }}
    />
  );
}
```

You pass in your own server-side fetch function. The component never sees your API key.

### `product-details`

A full product page: image gallery, variant picker (with in-stock and out-of-stock states), merchant offers, price history chart, and a similar-products carousel.

```tsx theme={"theme":{"light":"github-light","dark":"github-dark"}}
import { ProductDetails } from "@/components/channel3/product-details";

export default async function ProductPage({ params }: { params: { id: string } }) {
  const product = await channel3Client.products.retrieve(params.id);

  return (
    <ProductDetails
      product={product}
      onFetchSimilar={async (id) => {
        "use server";
        return channel3Client.products.find_similar({ product_id: id });
      }}
    />
  );
}
```

## Building blocks

Every part of the blocks above is also available on its own:

| Component | What it does |
| - | - |
| `product-card` | A single product tile with image, title, price, and brand |
| `product-grid` | Responsive grid of `product-card` components |
| `product-carousel` | Horizontally scrolling product strip |
| `variant-selector` | Size, color, and option pickers with availability styling |
| `price-chart` | Price history chart from the price tracking API |
| `offer-list` | List of merchant offers for a product |
| `image-gallery` | Zoomable image carousel for a product page |
| `search-bar` | Text and image search input |
| `filter-panel` | Filter sidebar with options loaded on demand |

## Hooks

The hooks handle the logic behind the UI. Use them with your own markup when the default components don't match your design.

### `use-product-search`

Tracks the search query, filters, and pagination. Returns results, a loading flag, and functions to update filters or load more.

```tsx theme={"theme":{"light":"github-light","dark":"github-dark"}}
import { useProductSearch } from "@/hooks/channel3/use-product-search";

function SearchPage() {
  const { results, isLoading, setQuery, setFilters, loadMore } = useProductSearch({
    onSearch: async (params) => fetch("/api/search", { method: "POST", body: JSON.stringify(params) }).then(r => r.json()),
  });

  return (
    <>
      <input onChange={e => setQuery(e.target.value)} placeholder="Search products…" />
      {isLoading && <Spinner />}
      {results.map(p => <MyProductCard key={p.id} product={p} />)}
      <button onClick={loadMore}>Load more</button>
    </>
  );
}
```

### `use-variant-selection`

Handles variant picking: which options are available, which combinations exist, and what happens when the user changes a selection. Returns the current `selected` state after each server call — you don't have to compare the request and response yourself.

```tsx theme={"theme":{"light":"github-light","dark":"github-dark"}}
import { useVariantSelection } from "@/hooks/channel3/use-variant-selection";

function VariantPicker({ product }) {
  const { selected, options, select } = useVariantSelection({
    product,
    onFetch: async (id, opts) => fetch(`/api/products/${id}?${opts}`).then(r => r.json()),
  });

  return (
    <div>
      {options.map(opt => (
        <div key={opt.name}>
          <label>{opt.name}</label>
          {opt.values.map(val => (
            <button
              key={val.label}
              data-selected={selected[opt.name] === val.label}
              onClick={() => select(opt, val)}
            >
              {val.label}
            </button>
          ))}
        </div>
      ))}
    </div>
  );
}
```

### `use-async-options`

Loads category and brand filter options when needed, so filter panels stay fast even with large catalogs.

```tsx theme={"theme":{"light":"github-light","dark":"github-dark"}}
import { useAsyncOptions } from "@/hooks/channel3/use-async-options";

const { options: brandOptions, isLoading } = useAsyncOptions({
  fetch: () => channel3Client.brands.list({ limit: 50 }),
  map: brand => ({ label: brand.name, value: brand.id }),
});
```

## How it fits together

Components take Channel3 data as props and call your callbacks when the user does something. They don't call the API or read your key themselves. Fetch on your server where `CHANNEL3_API_KEY` lives, then pass the results down. That keeps your key safe and makes each component easy to test on its own.

<Card title="Quickstart" icon="rocket" href="/" arrow="true">
  Install the Channel3 skill and let your agent wire the components in for you.
</Card>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.