---
title: "Anthropic"
description: "Use GistUI with Claude through Anthropic's Messages API, with plain fetch or the official SDK."
---

> 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.

# Anthropic

With the Messages API the system prompt is its own field, `system`, not a message.

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

## With fetch

```ts title="api/answer.ts"
import { prompt } from "@gistui/catalog";
import { textDeltas, toReadable } from "@gistui/server";

export async function POST(req: Request) {
  const { messages } = await req.json();

  const res = await fetch("https://api.anthropic.com/v1/messages", {
    method: "POST",
    headers: {
      "x-api-key": process.env.ANTHROPIC_API_KEY!,
      "anthropic-version": "2023-06-01",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      model: "claude-sonnet-5-5",
      max_tokens: 4096,
      stream: true,
      system: prompt().text,
      messages,
    }),
  });

  if (!res.ok) return new Response(await res.text(), { status: res.status });
  return new Response(toReadable(textDeltas(res, "anthropic")));
}
```

## With `@anthropic-ai/sdk`

```ts title="api/answer.ts"
import Anthropic from "@anthropic-ai/sdk";
import { prompt } from "@gistui/catalog";
import { toReadable } from "@gistui/server";

const client = new Anthropic();

export async function POST(req: Request) {
  const { messages } = await req.json();

  const stream = client.messages.stream({
    model: "claude-sonnet-5-5",
    max_tokens: 4096,
    system: prompt().text,
    messages,
  });

  async function* text() {
    for await (const event of stream) {
      if (event.type === "content_block_delta" && event.delta.type === "text_delta") yield event.delta.text;
    }
  }
  return new Response(toReadable(text()));
}
```

## Prompt caching

GistUI's prompt is byte-stable, so it is a good cache target. Mark it as cacheable and put anything that changes per request (the tool list, the user's context) after it.

```ts
system: [{ type: "text", text: prompt().text, cache_control: { type: "ephemeral" } }],
```

## Render it

```tsx
import "@gistui/styles/styles.css";
import { GistUI } from "@gistui/react";
import { ui } from "@gistui/react/ui";

const res = await fetch("/api/answer", { method: "POST", body: JSON.stringify({ messages }) });
<GistUI library={ui} stream={res.body} onAction={handle} />;
```

## In a chat

```ts
import { anthropic, createChat } from "@gistui/chat";

// url: your own endpoint that forwards to Anthropic and adds the key and the system prompt.
const store = createChat({ adapter: anthropic({ model: "claude-sonnet-5-5", url: "/api/chat" }) });
```

Source: https://gistui.com/docs/models/anthropic/index.mdx
