Yak Docs
SDKs

React SDK

The @yak-io/react package provides React components and hooks for integrating Yak into any React application.

For Next.js applications, use @yak-io/nextjs instead for automatic route scanning and App Router integration.

Installation

Works with React 17, 18, and 19.

npm install @yak-io/react @yak-io/javascript
pnpm add @yak-io/react @yak-io/javascript
yarn add @yak-io/react @yak-io/javascript
bun add @yak-io/react @yak-io/javascript

Quick Start

Add the Provider and Widget

// App.tsx
import { YakProvider, YakWidget } from "@yak-io/react";

export default function App() {
  return (
    <YakProvider
      appId="your-app-id"
      getConfig={async () => {
        const res = await fetch("/api/yak");
        return res.json();
      }}
      onToolCall={async (name, args) => {
        const res = await fetch("/api/yak", {
          method: "POST",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify({ name, args }),
        });
        const data = await res.json();
        if (!data.ok) throw new Error(data.error);
        return data.result;
      }}
    >
      {/* Your app content */}
      <YakWidget />
    </YakProvider>
  );
}

Set up server handlers

Use @yak-io/javascript to create the API endpoints on your backend. See the JavaScript SDK for runtime-specific examples (Express, Hono, Cloudflare Workers, etc.).

// api/yak.ts
import { createYakHandler } from "@yak-io/javascript/server";

export const { GET, POST } = createYakHandler({
  routes: [
    { path: "/", title: "Home" },
    { path: "/products", title: "Products" },
  ],
});

Components

YakProvider

Wraps your application and manages widget state and communication.

PropTypeRequiredDescription
appIdstringYesYour Yak application ID
mode"chat" | "voice" | "both"NoWhich surfaces the trigger exposes. Defaults to "chat". See Voice Mode.
getConfig() => Promise<ChatConfig>NoConfig provider for routes and tools (used by chat and voice)
onToolCall(name, args) => Promise<unknown>NoHandler for tool execution (used by chat and voice)
themeThemeNoWidget styling options
onRedirect(path: string) => voidNoCustom navigation handler
disableRestartButtonbooleanNoHide the restart button in the header
disablePageContentbooleanNoStop sending any page context (URL, title, and visible text) to the assistant. The widget still works, but it won't be aware of the page the user is on.
user{ id, hash }NoSigned end-user identity. Enables server-side conversation persistence and the history pane.

Privacy: By default Yak shares the current page's URL, title, and visible text with the assistant so it can answer questions about the page the user is viewing. Set disablePageContent to turn this off entirely — the SDK then sends nothing about the page (not even the URL), so the assistant can't answer page-specific questions.

YakWidget

Renders the trigger pill — logo plus one or two icon buttons depending on mode.

PropTypeDefaultDescription
mode"chat" | "voice" | "both"inherited from providerOverride the provider mode for this trigger
positionWidgetPosition"bottom-right"One of nine positions (e.g. "bottom-left", "top-center")
colorMode"light" | "dark" | "system"Force a color mode for the pill
lightButton{ background?, color?, border? }Custom pill colors in light mode
darkButton{ background?, color?, border? }Custom pill colors in dark mode

Hooks

useYak

Access widget controls from any component inside YakProvider:

import { useYak } from "@yak-io/react";

export function ChatButton() {
  const { open, close, openWithPrompt, isOpen } = useYak();

  return (
    <div>
      <button onClick={() => open()}>Open Chat</button>
      <button onClick={() => openWithPrompt("Help me with this page")}>
        Get Help
      </button>
      {isOpen && <button onClick={() => close()}>Close</button>}
    </div>
  );
}
PropertyTypeDescription
mode"chat" | "voice" | "both"The current mode the provider was configured with
open() => voidOpen the chat panel
close() => voidClose the chat panel
openWithPrompt(prompt: string) => voidOpen and send a specific prompt
isOpenbooleanWhether the chat panel is currently open
isReadybooleanWhether the chat iframe has loaded and can receive messages
chatLoadingbooleanisOpen && !isReady — the panel is opening but not yet interactive
voiceStateVoiceState"idle" | "connecting" | "listening" | "thinking" | "speaking" | "error"
voiceIsActivebooleantrue while a voice session is live
voiceLoadingbooleantrue while the voice session is connecting
voiceStart()() => Promise<void>Start a voice session (call from a user gesture)
voiceStop()() => Promise<void>Stop the current voice session
voiceToggle()() => Promise<void>Start if idle/error, stop if active

See Voice Mode for the full voice API and behavior.

useYakToolEvent

Subscribe to tool call completion events — useful for keeping your UI in sync with agent actions:

import { useYakToolEvent } from "@yak-io/react";

function OrderPage({ orderId }: { orderId: string }) {
  const queryClient = useQueryClient();

  useYakToolEvent((event) => {
    if (event.ok && event.name.startsWith("order.")) {
      queryClient.invalidateQueries({ queryKey: ["order", orderId] });
    }
  });

  return <OrderDetails orderId={orderId} />;
}

See UI Synchronization for more details.

Router Integration

Pass your framework's navigation function to onRedirect for client-side navigation:

// React Router
import { useNavigate } from "react-router-dom";

function App() {
  const navigate = useNavigate();

  return (
    <YakProvider onRedirect={(path) => navigate(path)} {...props}>
      {children}
    </YakProvider>
  );
}

// TanStack Router
<YakProvider onRedirect={(path) => router.navigate({ to: path })} />

Next Steps

On this page