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
| Property | Type | Description |
|---|---|---|
open | () => void | Open the chat panel |
close | () => void | Close the chat panel |
openWithPrompt | (prompt: string) => void | Open and send a specific prompt |
isOpen | boolean | Whether the chat panel is currently open |
isReady | boolean | Whether the chat iframe has loaded and can receive messages |
chatLoading | boolean | isOpen && !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).