Yak Docs
Customization

Programmatic Control

Control the chat widget programmatically from anywhere in your application using the useYak hook.

Basic Usage

import { useYak } from "@yak-io/nextjs/client";
// or: import { useYak } from "@yak-io/react";

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

  return (
    <div>
      <button onClick={() => open()}>Open Chat</button>
      <button onClick={() => openWithPrompt("Help me!")}>Get Help</button>
      {isOpen && <button onClick={() => close()}>Close</button>}
    </div>
  );
}

useYak API

PropertyTypeDescription
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

Prompts sent via openWithPrompt are automatically queued if the widget isn't ready yet.

Custom Loading State

When you replace the built-in trigger with your own button, use chatLoading to show a spinner while the chat iframe boots. It's true from the moment the panel opens until the iframe is ready — so you don't have to derive isOpen && !isReady by hand.

import { useYak } from "@yak-io/react";
import { Loader2, MessageSquare } from "lucide-react";

export function ChatLauncher() {
  const { open, close, isOpen, chatLoading } = useYak();

  return (
    <button
      onClick={() => (isOpen ? close() : open())}
      disabled={chatLoading}
      aria-label={chatLoading ? "Loading chat" : isOpen ? "Close chat" : "Open chat"}
    >
      {chatLoading ? <Loader2 className="animate-spin" /> : <MessageSquare />}
    </button>
  );
}

Voice exposes the same idea: voiceLoading is true while a session is connecting. See Voice Mode.

Common Patterns

Context-Sensitive Help

Open with prompts tailored to the current page:

"use client";

import { useYak } from "@yak-io/nextjs/client";
import { usePathname } from "next/navigation";

export function ContextualHelpButton() {
  const { openWithPrompt } = useYak();
  const pathname = usePathname();

  const getHelpPrompt = () => {
    if (pathname.includes("/billing")) return "Help me with billing";
    if (pathname.includes("/settings")) return "Guide me through settings";
    return "Help me with this page";
  };

  return (
    <button onClick={() => openWithPrompt(getHelpPrompt())}>
      Get Help
    </button>
  );
}

Error Assistance

Offer AI help when errors occur:

function ErrorFallback({ error }: { error: Error }) {
  const { openWithPrompt } = useYak();

  return (
    <div>
      <h2>Something went wrong</h2>
      <p>{error.message}</p>
      <button
        onClick={() => openWithPrompt(`Help me fix: ${error.message}`)}
      >
        Get AI Assistance
      </button>
    </div>
  );
}

Feature Onboarding

Guide users through features:

function FeatureCard({ title, description }: Props) {
  const { openWithPrompt } = useYak();

  return (
    <div className="feature-card">
      <h3>{title}</h3>
      <p>{description}</p>
      <button onClick={() => openWithPrompt(`Show me how to use ${title}`)}>
        Learn More
      </button>
    </div>
  );
}

Keyboard Shortcuts

Toggle the chat with a keyboard shortcut:

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

function useYakShortcut() {
  const { open, close, isOpen } = useYak();

  useEffect(() => {
    function handleKeyDown(e: KeyboardEvent) {
      if ((e.metaKey || e.ctrlKey) && e.key === "k") {
        e.preventDefault();
        isOpen ? close() : open();
      }
      if (e.key === "Escape" && isOpen) {
        close();
      }
    }

    window.addEventListener("keydown", handleKeyDown);
    return () => window.removeEventListener("keydown", handleKeyDown);
  }, [open, close, isOpen]);
}

Help Menu

Create a help menu with predefined prompts:

const helpTopics = [
  { label: "Getting Started", prompt: "Show me how to get started" },
  { label: "Account Settings", prompt: "Help me configure my account" },
  { label: "Billing Questions", prompt: "I have questions about billing" },
];

function HelpMenu() {
  const { openWithPrompt } = useYak();

  return (
    <div className="help-menu">
      <h3>How can we help?</h3>
      <ul>
        {helpTopics.map((topic) => (
          <li key={topic.label}>
            <button onClick={() => openWithPrompt(topic.prompt)}>
              {topic.label}
            </button>
          </li>
        ))}
      </ul>
    </div>
  );
}

Best Practices

Use specific, contextual prompts:

// Good — specific and actionable
openWithPrompt("How do I export my project data to CSV?");

// Less effective — too vague
openWithPrompt("Help");

Avoid auto-opening without user action:

// Good — user-initiated
<button onClick={() => open()}>Need Help?</button>

// Use sparingly — only for critical situations
useEffect(() => {
  if (isCriticalError) {
    openWithPrompt("Help me resolve this error");
  }
}, [isCriticalError]);

Don't include sensitive data in prompts (passwords, tokens, personal information).

On this page