---
title: "Vercel AI SDK"
description: "Use GistUI with streamText, useChat and the AI SDK's UI message stream, in a full-screen answer or a chat."
---

> Documentation Index
> Fetch the complete documentation index at: https://gistui.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Vercel AI SDK

`streamText` works as it is. Pass GistUI's prompt as `system` and choose how to return the stream.

| You render with | Return | Client |
|---|---|---|
| `<GistUI stream>` | `result.toTextStreamResponse()` | `stream={res.body}` |
| `useChat` from `@ai-sdk/react` | `result.toUIMessageStreamResponse()` | `source` + `streaming` from the message text |
| `@gistui/chat` | `result.toUIMessageStreamResponse()` | the `aiSdk()` adapter |

```sh
npm i ai @gistui/catalog
yarn add ai @gistui/catalog
pnpm add ai @gistui/catalog
bun add ai @gistui/catalog
```

## A single answer

```ts title="app/api/answer/route.ts"
import { prompt } from "@gistui/catalog";
import { convertToModelMessages, streamText } from "ai";

export async function POST(req: Request) {
  const { messages } = await req.json();
  const result = streamText({
    model: "openai/gpt-5", // a gateway model id, or a provider model such as openai("gpt-5")
    system: prompt().text,
    messages: await convertToModelMessages(messages),
  });
  return result.toTextStreamResponse();
}
```

```tsx title="app/answer.tsx"
"use client";
import "@gistui/styles/styles.css";
import { GistUI } from "@gistui/react";
import { ui } from "@gistui/react/ui";

export function Answer({ stream }: { stream: ReadableStream }) {
  return <GistUI library={ui} stream={stream} />;
}
```

## A chat with `useChat`

Use the inline prompt, so a reply is chat text with the UI in `gistui` code fences. `splitReply` separates the two.

```ts title="app/api/chat/route.ts"
import { prompt } from "@gistui/catalog";
import { convertToModelMessages, streamText } from "ai";

export async function POST(req: Request) {
  const { messages } = await req.json();
  const result = streamText({
    model: "openai/gpt-5",
    system: prompt({ mode: "inline" }).text,
    messages: await convertToModelMessages(messages),
  });
  return result.toUIMessageStreamResponse();
}
```

```tsx title="app/chat.tsx"
"use client";
import "@gistui/styles/styles.css";
import { useChat } from "@ai-sdk/react";
import { replyProgram, splitReply } from "@gistui/chat";
import { GistUI } from "@gistui/react";
import { ui } from "@gistui/react/ui";

export function Chat() {
  const { messages, sendMessage, status } = useChat();

  return messages.map((m, i) => {
    const text = m.parts.filter((p) => p.type === "text").map((p) => p.text).join("");
    if (m.role === "user") return <p key={m.id}>{text}</p>;

    const streaming = status === "streaming" && i === messages.length - 1;
    const parts = splitReply(text, { streaming }); // [{ kind: "text", text }, { kind: "ui", source, open }]
    return (
      <div key={m.id}>
        {parts.filter((p) => p.kind === "text").map((p, j) => <p key={j}>{p.text}</p>)}
        <GistUI
          library={ui}
          source={replyProgram(parts)}
          streaming={streaming}
          onAction={(a) => (a.type === "send" || a.type === "submit") && sendMessage({ text: a.message })}
        />
      </div>
    );
  });
}
```

All the fences of one reply form one program, shown where the first fence is.

## A chat with `@gistui/chat`

If you would rather not hold the conversation yourself, `@gistui/chat` has a store and ready React layouts. The same route as above works.

```tsx
import { aiSdk, createChat, localThread } from "@gistui/chat";
import { Chat } from "@gistui/chat/react";
import "@gistui/chat/chat.css";
import "@gistui/styles/styles.css";
import { ui } from "@gistui/react/ui";

const store = createChat({ adapter: aiSdk({ url: "/api/chat" }), storage: localThread("support") });

<Chat store={store} library={ui} layout="sidebar" title="Assistant" />;
```

See [Chat](/docs/guides/chat/).

## Tools

AI SDK tools (`tools: { … }` in `streamText`) run on your server while the model is answering, and they keep working unchanged. GistUI's own `tools` are different: they are called from the rendered UI, in the browser, by `@query`. Use them when an answer should keep its data live after the model has finished. See [Tools and live data](/docs/guides/tools-and-data/).

## Repair on the server

To store the repaired program rather than the raw one, pass the text stream through `repairStream`:

```ts
import { library, prompt } from "@gistui/catalog";
import { repairStream, toReadable } from "@gistui/server";

const result = streamText({ model, system: prompt().text, messages });
return new Response(toReadable(repairStream(result.textStream, library)));
```

Source: https://gistui.com/docs/models/vercel-ai-sdk/index.mdx
