Skip to main content
The web components are framework-agnostic custom elements, so React renders them as ordinary tags and the SDK script upgrades them in the browser. Nothing needs a wrapper library. This page walks a complete install on a Next.js App Router project with a dark storefront. Adapt the palette and the route names; everything else transfers.

Before you start

Complete the Quickstart prerequisites, and pay particular attention to origin registration — it is the step that most often blocks a framework install:
Origins are matched as exact strings. On a platform that mints a new hostname per deployment — Vercel and Netlify preview URLs, for example — every preview origin is a different origin and will be refused. Register the stable alias and develop against that, or against a registered localhost port. A refused origin surfaces in the component as Failed to fetch.

1. Load the script once, in the root layout

One bundle registers both <bonsai-search> and <bonsai-searchbar>, so a single tag in the root layout covers every page. Do not add a second script for the second component.

2. Declare the tags for TypeScript

Without this, JSX reports the custom elements as unknown. Declare them once.
Neither component needs "use client". They are plain tags in the server-rendered HTML, and the script upgrades them in the browser. Add "use client" only if that particular component also attaches event listeners or calls the imperative methods.

3. Put the palette in global CSS

Set the variables on the elements themselves — a value inherited from :root or a wrapper loses to the SDK’s own :host declaration. The block below is a dark palette on a pure black canvas with a monospace face. theme="light" on the tags in the next steps is what keeps these lines editable; see Dark theme.

4. The search bar, in your header

On submit it navigates to /ai-search?q=…. Placeholder text and suggestion chips come from the Settings API, so leave them off the tag and manage them in the dashboard. To put the bar behind a trigger instead — an icon or an “AI” button in the header — add close-button and listen for the close event, which is where that pattern is written out in full.

5. The results route

<bonsai-search> reads ?q= from the URL itself, prefills the input and runs the search. There is nothing to wire between the two components — the search bar’s search-path just has to point at this route.
No base-url: the API origin defaults to production and the path comes from your organization’s configured search version. See base-url if you need to pin one.

6. The pixel

Add the pixel to the same root layout with strategy="beforeInteractive", so it runs before the components look for a shopper identifier. Set cookieDomain to your registrable domain once the app is on its real host; pass "" while it is still on a platform subdomain.

Reading results in React

If you need the results in your own component — analytics, a custom empty state — listen for the events. They bubble and are composed, so a ref on the element or a listener on an ancestor both work. This is the case that needs "use client".

Vite, Remix, and plain React

The same five pieces apply, only the script tag moves. Put it in index.html for Vite, in the root route’s <Links>/<Scripts> region for Remix, or in public/index.html for Create React App. The TypeScript declaration, the CSS, the tags and the ?q= contract are identical.