Ask AI chat panel

以 Markdown 格式查看

AskAiChat renders a sidebar panel with AI chat (no keyword search). It’s one of the two components in the @fern-api/search-widget package, which covers the prerequisites and the Content Security Policy the widget needs.

The AskAiChat component embedded in a marketing site.

Setup

1

Install the package

$npm install @fern-api/search-widget react@19 react-dom@19
2

Render the chat

Import the component and the bundled styles, then render it with your docs domain. By default, the component renders its own trigger button, and citations in its answers link to your docs domain and open in a new tab. cmd/ctrl + / toggles the panel, and it becomes a bottom drawer below 768px.

1// Required in the Next.js App Router
2'use client';
3
4import { AskAiChat } from '@fern-api/search-widget';
5import '@fern-api/search-widget/styles';
6
7export function AskAi() {
8 return <AskAiChat domain="https://docs.example.com" />;
9}
3

Match the chat to your product

theme controls light and dark mode, and accentColor sets the accent used for the sparkles, the send button, and links in answers, in both modes. trigger chooses between the corner launcher (floating), an unpainted button you place and style yourself (inline), and no button at all (none).

1<AskAiChat domain="https://docs.example.com" theme="inherit" accentColor="#6b4eff" />

Custom triggers

A dropdown menu item can hold the trigger, but not the whole widget: selecting the item closes the menu, and most menu libraries unmount the menu’s contents when it closes, destroying the chat before it renders.

Split the widget into AskAiChat.Root, AskAiChat.Trigger, and AskAiChat.Panel instead, keeping the trigger inside the menu and the chat outside it. The single <AskAiChat /> component remains the normal integration.

1import { AskAiChat } from '@fern-api/search-widget';
2import '@fern-api/search-widget/styles';
3
4function HelpMenu() {
5 return (
6 <AskAiChat.Root domain="https://docs.example.com">
7 <DropdownMenu.Root>
8 <DropdownMenu.Trigger>Help</DropdownMenu.Trigger>
9 <DropdownMenu.Portal>
10 <DropdownMenu.Content>
11 <DropdownMenu.Item>Contact support</DropdownMenu.Item>
12 <DropdownMenu.Item asChild>
13 <AskAiChat.Trigger />
14 </DropdownMenu.Item>
15 </DropdownMenu.Content>
16 </DropdownMenu.Portal>
17 </DropdownMenu.Root>
18 <AskAiChat.Panel />
19 </AskAiChat.Root>
20 );
21}
  • AskAiChat.Root holds the state and takes the configuration props: domain, lang, searchLocale, placement, theme, accentColor, open, defaultOpen, and onOpenChange. Its children is the subtree to wrap, the menu and the panel.
  • AskAiChat.Trigger is a real <button>, so asChild works and the menu keeps its keyboard behavior. Styling props (className, style, icon, and any button attributes) go here. variant="floating" renders the corner launcher instead of an unpainted button.
  • AskAiChat.Panel is the chat. Render exactly one per root, outside any menu.

The same shape covers a popover, a command palette, or a nav bar dropdown, and a single root supports multiple triggers, at most one of them variant="floating".

Don’t keep the menu open with onSelect={(event) => event.preventDefault()} as a workaround. A modal menu sets pointer-events: none on <body>, and the chat, portaled to <body>, inherits it: the chat paints correctly but ignores all pointer and keyboard input. Let the menu close instead.

If the trigger is an element you already have (a link, a list row, a command palette entry), set trigger="none" so the widget renders no button, and drive open yourself:

1import { AskAiChat } from '@fern-api/search-widget';
2import '@fern-api/search-widget/styles';
3
4const [askAiOpen, setAskAiOpen] = useState(false);
5
6<a href="#" onClick={() => setAskAiOpen(true)}>
7 <AskAiChat.Sparkles /> Still stuck? Ask AI
8</a>
9
10<AskAiChat
11 domain="https://docs.example.com"
12 trigger="none"
13 open={askAiOpen}
14 onOpenChange={setAskAiOpen}
15/>

Wire onOpenChange back to your state, or the chat’s own controls (its close button, Escape, and cmd/ctrl + /) will appear to do nothing. Pass neither prop and the widget manages its own state. With trigger="none", the trigger’s accessibility attributes (aria-haspopup="dialog", plus aria-expanded on a button) are yours to provide.

AskAiChat.Sparkles is the sparkle icon the built-in trigger uses, for making your own trigger read as the same affordance. It sizes to 1em of the surrounding text and takes its color.

Properties

All standard HTML button attributes are also supported and forwarded to the trigger button.

domain
stringRequired

The URL of your published Fern Docs site (for example, https://docs.example.com). Include the full path if your docs aren’t at the root (for example, https://buildwithfern.com/learn).

lang
stringDefaults to en

Language code for the interface.

icon
React.ReactNode

Icon element to display in the trigger button.

children
React.ReactNode

Trigger button content (text, icons, etc.).

theme
'inherit' | 'light' | 'dark' | 'auto'Defaults to inherit

inherit follows the host page’s theme (a dark class or data-theme="dark" on <html> or <body>) and switches live with it. light and dark pin the widget to that mode. auto follows the visitor’s prefers-color-scheme.

accentColor
string

Accent color for the sparkles, send button, and links in answers. Accepts any CSS color (for example, #6b4eff), and derives the light and dark variants from it. Defaults to a neutral gray.

trigger
'floating' | 'inline' | 'none'Defaults to floating

floating renders a styled launcher pinned to a corner of the viewport. inline renders an unpainted button to place and style yourself, such as in a nav bar. Both carry the sparkle icon and an Ask AI label by default. none renders no trigger at all, for driving the open state from your own UI.

open
boolean

Controls the open state instead of letting the widget own it. Wire onOpenChange back to your state when passing this prop.

defaultOpen
boolean

The initial open state when uncontrolled. Ignored once open is passed.

onOpenChange
(open: boolean) => void

Fires whenever the chat wants to open or close, from any source: the trigger, the close button, Escape, or cmd/ctrl + /. Called in both controlled and uncontrolled mode.

placement
'bottom-right' | 'bottom-left' | 'top-right' | 'top-left'Defaults to bottom-right

The corner the launcher and card anchor to.

searchLocale
string

Locale used for retrieval. Defaults to lang.