ProductReadyProductReady

Ask AI Widget

AI-powered docs Q&A widget that searches your documentation and answers user questions

Ask AI Widget

Why

Users visiting your site often have questions but don't want to dig through documentation pages. The Ask AI widget provides an instant, conversational way to get answers — powered by your existing Fumadocs content.

Instead of browsing docs manually, users type a question and get a concise AI-generated answer based on your actual documentation.

What

A compact floating widget (bottom-right corner) that:

  1. Extracts keywords from the user's question using a fast model (gpt-5-nano)
  2. Searches your docs via the existing Fumadocs /api/search endpoint
  3. Streams an AI answer based on the search results using gpt-5-mini

The widget ships as a shared package (share-domains/ask-ai) with two parts:

  • components/ — Client-side chat widget built on KUI AI elements + AI SDK v6
  • logic/ — Server-side utilities (keyword extraction, doc search, prompt building)

UI Behavior

StateAppearance
CollapsedTiny input bar with 💬 icon + "Ask AI" placeholder
FocusedInput expands to 320px, empty chat panel appears
ActiveChat panel shows conversation, input stays expanded
Escape / CloseCollapses back to tiny input bar

How

1. Environment Variables

Add to your .env:

# Required: Enable the widget
ENABLE_ASK_AI=true

# Required: OpenAI API access
OPENAI_API_KEY=sk-...
OPENAI_BASE_URL=https://api.openai.com/v1  # optional, for proxies

# Optional: Override default models
ASK_AI_MODEL=gpt-5-mini          # Answer generation (default: gpt-5-mini)
ASK_AI_FAST_MODEL=gpt-5-nano     # Keyword extraction (default: gpt-5-nano)

# Required: App URL for internal search API calls
NEXT_PUBLIC_APP_URL=http://localhost:3000

2. Add Tailwind Source

In your app's global.css, add the share-domains source so Tailwind scans the widget classes:

@source "../../../../packages/share-domains";

3. Create the API Route

Create src/app/api/search/ai/route.ts:

import { createOpenAI } from "@ai-sdk/openai";
import { streamText } from "ai";
import { buildSystemPrompt, extractKeywords, searchDocs } from "share-domains/ask-ai/logic";

export async function POST(req: Request) {
  const { messages } = await req.json();
  const lastUserMessage = [...messages].reverse().find((m) => m.role === "user");
  const question = lastUserMessage?.content || "";

  const openai = createOpenAI({
    apiKey: process.env.OPENAI_API_KEY,
    baseURL: process.env.OPENAI_BASE_URL || undefined,
  });

  const baseUrl = process.env.NEXT_PUBLIC_APP_URL || "http://localhost:3000";
  const keywords = await extractKeywords(openai(process.env.ASK_AI_FAST_MODEL || "gpt-5-nano"), question);
  const docsContext = await searchDocs(baseUrl, keywords);

  const result = streamText({
    model: openai(process.env.ASK_AI_MODEL || "gpt-5-mini"),
    system: buildSystemPrompt(docsContext),
    messages,
  });

  return result.toUIMessageStreamResponse();
}

4. Add Widget to Layout

In your root layout.tsx (server component):

import { AskAIWidget } from "share-domains/ask-ai/components";

export default function RootLayout({ children }) {
  return (
    <html lang="en">
      <body>
        {children}
        {process.env.ENABLE_ASK_AI === "true" && (
          <AskAIWidget
            endpoint="/api/search/ai"
            title="Ask MyApp"
            placeholder="Ask AI"
            welcomeMessage="Ask me anything!"
          />
        )}
      </body>
    </html>
  );
}

Configuration Props

PropTypeDefaultDescription
endpointstring/api/search/aiAPI route path
placeholderstringAsk AIInput placeholder text
titlestringAI AssistantChat panel header title
welcomeMessagestring""Message shown when panel is empty
maxHeightstring480pxMax height of the chat panel

Architecture

User types question
  → [Client] AskAIWidget (useChat + DefaultChatTransport)
  → [Server] POST /api/search/ai
      → extractKeywords(gpt-5-nano, question)  →  "i18n config setup"
      → searchDocs(baseUrl, keywords)           →  fumadocs /api/search
      → streamText(gpt-5-mini, docs context)    →  streamed answer
  → [Client] MessageResponse renders markdown

Prerequisites

  • Fumadocs with /api/search route configured
  • @ai-sdk/openai, ai packages installed
  • share-domains as workspace dependency
  • kui as workspace dependency (for AI elements)

On this page