Skip to main content

AI Search React/TSX Implementation

Log in to add to favourites

Page last updated 05 October 2026

This guide walks through integrating the insytful-ai-search-components widgets into an existing React application, from install to production-ready configuration.

1. Install

JSX
npm install insytful-ai-search-components

The package needs react and react-dom 17 or later, which your project should already have.

Import the stylesheet once, near your app's entry point:

import "insytful-ai-search-components/style.css"

2. Add the search

JSX
import { Theme, InsytfulSearch } from "insytful-ai-search-components";

export function SiteSearch() {
  return (
    <Theme>
      <InsytfulSearch.Root
        options={{ config: "your-config", baseUrl: "https://api.example.com" }}
      >
        <InsytfulSearch.Trigger>Search</InsytfulSearch.Trigger>

        <InsytfulSearch.Portal>
          <InsytfulSearch.Close />
          <InsytfulSearch.Title>How can we help?</InsytfulSearch.Title>
          <InsytfulSearch.Description>
            Ask a question and get an AI-generated answer.
          </InsytfulSearch.Description>
          <InsytfulSearch.Input placeholder="Ask a question..." />
          <InsytfulSearch.Messages />
          <InsytfulSearch.Suggestions
            items={["How do I get started?", "What does this product do?"]}
          />
          <InsytfulSearch.Disclaimer>
            AI-generated answers may be inaccurate.
          </InsytfulSearch.Disclaimer>
        </InsytfulSearch.Portal>
      </InsytfulSearch.Root>
    </Theme>
  );
}

The Trigger button renders where you place it, for example in your header. The dialog in Portal opens on top of the page, so your layout and z-index don't affect it.

Only Root, Portal, Input and Messages are required. Everything else is optional.

<Theme> gives the components their default look. Without it they render unstyled, for you to style yourself (see Theming).

3. Configuration

options on Root needs two values from your account manager:

JSX
options={{
  config: "your-config",              // your Insytful config ID
  baseUrl: "https://api.example.com", // your AI Search API URL
}}

On public sites you can add a reCAPTCHA v3 site key to help block automated abuse:

JavaScript
options={{ config: "…", baseUrl: "…", recaptchaSiteKey: "your-site-key" }}

reCAPTCHA v3 runs in the background, so visitors never see a challenge. Each question is sent with a token, and the AI Search API checks it.

4. Show answers as formatted text

Answers arrive as markdown. To show headings, lists and links, pass a markdown renderer to Root:

JSX
import ReactMarkdown from "react-markdown";

<InsytfulSearch.Root
  options={…}
  renderMarkdown={(content) => <ReactMarkdown>{content}</ReactMarkdown>}
>

Without it, answers show as plain text. The package doesn't include a renderer, so you choose one. react-markdown is a safe default because it doesn't render raw HTML unless you turn that on.

5. Open from your own button

To open the search from somewhere else, such as a keyboard shortcut or a link in your navigation, control the open state yourself:

JSX
const [open, setOpen] = useState(false);

<button onClick={() => setOpen(true)}>Ask AI</button>

<InsytfulSearch.Root open={open} onOpenChange={setOpen} options={…}>
  <InsytfulSearch.Portal>…</InsytfulSearch.Portal>
</InsytfulSearch.Root>

Trigger is optional when you do this. Closing the dialog keeps the conversation; reloading the page starts a new one.

Sticky headers

If your site has a sticky header, mark it so the dialog opens below it:

<header data-insytful-offset>…</header>

You can mark more than one element, such as a header and a cookie banner, and their heights are added together. The dialog follows any height changes automatically.

6. Handling errors

Show an error message by reading error from the search context:

JSX
function ErrorArea() {
  const { error } = InsytfulSearch.useSearchContext("ErrorArea");
  return error ? (
    <InsytfulSearch.ErrorCallout
      title="We couldn't get an answer"
      cta={{ text: "Contact us", path: "/contact" }}
    />
  ) : null;
}

Place <ErrorArea /> inside Portal, below Messages. title, text and cta are all optional and have sensible defaults.

7. Add a classic search fallback

If you also have a traditional search results page, let visitors switch between AI answers and your results:

JSX
<InsytfulSearch.Modes defaultValue="ai">
  <InsytfulSearch.ModeSwitch>
    {({ mode, onSwitch }) => (
      <div role="group" aria-label="Search mode">
        <button aria-pressed={mode === "ai"} onClick={() => onSwitch("ai")}>AI answer</button>
        <button aria-pressed={mode === "classic"} onClick={() => onSwitch("classic")}>Search results</button>
      </div>
    )}
  </InsytfulSearch.ModeSwitch>

  <InsytfulSearch.Mode name="ai">
    <InsytfulSearch.Input />
    <InsytfulSearch.Messages />
  </InsytfulSearch.Mode>

  <InsytfulSearch.Mode name="classic" path="/search?q=">
    <InsytfulSearch.Input />
  </InsytfulSearch.Mode>
</InsytfulSearch.Modes>

In classic mode, submitting a question goes to path with the question added to the end, so path must end with the query parameter and =. To use your app's router instead of a full page load, pass onNavigate={(url) => navigate(url)}.

8. Show an answer above your search results

Search.Overview shows an AI answer inline, with no dialog. Put it above your existing results and pass it the visitor's search:

JSX
<Theme>
  <InsytfulSearch.Overview
    options={{ config: "your-config", baseUrl: "https://api.example.com" }}
    term={query}
    renderMarkdown={(content) => <ReactMarkdown>{content}</ReactMarkdown>}
  />
</Theme>

The answer starts as soon as term is set. Long answers are shortened behind a "Show more" button. For a follow-up conversation in place of a single answer, add type="conversational".

renderMarkdown is required here: without it the answer text doesn't appear.

9. Theming

Override the design tokens on .insytful-theme in your own stylesheet:

JSX
.insytful-theme {
  --insytful-brand-primary: #5128c3;
  --insytful-font-family: "Helvetica Neue", Arial, sans-serif;
}

The dialog renders in a Shadow DOM, which your stylesheet can't reach. To style it, pass the CSS to <Theme> instead:

JSX
<Theme css={`.insytful-theme { --insytful-brand-primary: #5128c3; }`}>

You don't need !important. See AI Search theming for the common tokens, and the full reference for all of them.

10. Collect answer feedback

Add a report link and helpful / unhelpful buttons under each answer:

JSX
<InsytfulSearch.Messages
  feedback={{
    report: { text: "Report an error", href: "/report" },
    onVote: (vote, { mid }) => analytics.track("ai_vote", { vote, mid }),
  }}
/>

Votes are sent to Insytful automatically. Use onVote only if you want to record them in your own analytics too. Search.Overview takes the same feedback prop.

11. Quick-action buttons

Answers can come with quick-action buttons (call, email, link or a custom event), which are set up in the CMS. Call, email and link buttons work without any setup.

An event button sends a named event that you listen for on the page. Use it to open a web chat or a booking form, for example:

JavaScript
<script>
  (window.insytfulAISearchEvents ??= new EventTarget())
    .addEventListener("openWebChat", (e) => {
      // e.detail has the button's configured data
    });
</script>

To track every click, pass onCtaClick to Root.

12. Build without a backend

Add isDevMode to Root and the search returns a sample answer instead of calling the API:

JSX
<InsytfulSearch.Root
  options={…}
  isDevMode={process.env.NODE_ENV === "development"}
>

The sample answer uses every markdown feature and includes one quick-action button of each type, which makes it useful for testing your styles. It waits 8 seconds before the first words appear so you can see the loading state.

Make sure dev mode is off in production. Tie it to an environment check, as above, rather than setting it to true.

Building your own UI

If neither the dialog nor Overview fits, the underlying hooks (useRAGResponse, useRAGConversation) are exported so you can build your own. See the full reference.

Still need help?

If you still need help after reading this article, don't hesitate to reach out to the Insytful community on Slack or raise a support ticket to get help from our team.
New support request