Yak Docs
Other Frameworks

Remix

Remix applications use the @yak-io/react package for the client-side widget and @yak-io/javascript for server handlers.

Installation

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

Client Setup

Add the provider and widget to your root layout:

// app/root.tsx
import { useNavigate } from "@remix-run/react";
import { YakProvider, YakWidget } from "@yak-io/react";

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

  return (
    <html>
      <body>
        <YakProvider
          appId={ENV.YAK_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;
          }}
          onRedirect={(path) => navigate(path)}
        >
          <Outlet />
          <YakWidget />
        </YakProvider>
      </body>
    </html>
  );
}

Server Handler

Create a resource route for the API:

// app/routes/api.yak.ts
import { json, type ActionFunctionArgs, type LoaderFunctionArgs } from "@remix-run/node";
import { createYakHandler } from "@yak-io/javascript/server";

const { GET, POST } = createYakHandler({
  routes: [
    { path: "/", title: "Home" },
    { path: "/dashboard", title: "Dashboard" },
    { path: "/settings", title: "Settings" },
  ],
  tools: [
    // Add your tool adapters here
  ],
});

export async function loader({ request }: LoaderFunctionArgs) {
  const response = await GET(request);
  const data = await response.json();
  return json(data);
}

export async function action({ request }: ActionFunctionArgs) {
  const response = await POST(request);
  const data = await response.json();
  return json(data);
}

Dynamic Routes

Fetch routes from your Remix configuration or a CMS:

// app/routes/api.yak.ts
import { getRoutes } from "~/utils/routes";

export async function loader({ request }: LoaderFunctionArgs) {
  const routes = await getRoutes();
  
  const { GET } = createYakHandler({
    routes,
  });
  
  const response = await GET(request);
  const data = await response.json();
  return json(data);
}

Adding Tools

Integrate with your data layer:

// app/routes/api.yak.ts
import { db } from "~/utils/db.server";

const databaseTools = {
  id: "database",
  getTools: async () => [
    {
      name: "db.getUser",
      description: "Get user profile information",
      input_schema: {
        type: "object",
        properties: { userId: { type: "string" } },
        required: ["userId"],
      },
    },
  ],
  executeTool: async (name: string, args: Record<string, unknown>) => {
    if (name === "db.getUser") {
      return db.user.findUnique({ where: { id: args.userId as string } });
    }
    throw new Error(`Unknown tool: ${name}`);
  },
};

const { GET, POST } = createYakHandler({
  routes: [...],
  tools: [databaseTools],
});

Programmatic Control

Use the useYak hook in any component:

// app/components/HelpButton.tsx
import { useYak } from "@yak-io/react";

export function HelpButton() {
  const { openWithPrompt } = useYak();

  return (
    <button onClick={() => openWithPrompt("Help me with this page")}>
      Get Help
    </button>
  );
}

The onRedirect prop integrates with Remix's useNavigate for client-side navigation without full page reloads.

On this page