---
title: "Quickstart"
description: "Install GistUI, give your model the system prompt, and render its streamed answer as UI."
---

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

# Quickstart

By the end of this page a model's answer renders as a live interface in your app. You need a model you can call with streaming, and a web app in React, Vue, Svelte, Solid or vanilla JavaScript.

1. **Install**

   Install the renderer for your framework, the stylesheet and the catalog. All `@gistui/*` packages share one version: install and upgrade them together.

### React

```sh
npm i @gistui/react @gistui/styles @gistui/catalog
yarn add @gistui/react @gistui/styles @gistui/catalog
pnpm add @gistui/react @gistui/styles @gistui/catalog
bun add @gistui/react @gistui/styles @gistui/catalog
```
### Vue

```sh
npm i @gistui/vue @gistui/styles @gistui/catalog
yarn add @gistui/vue @gistui/styles @gistui/catalog
pnpm add @gistui/vue @gistui/styles @gistui/catalog
bun add @gistui/vue @gistui/styles @gistui/catalog
```
### Svelte

```sh
npm i @gistui/svelte @gistui/styles @gistui/catalog
yarn add @gistui/svelte @gistui/styles @gistui/catalog
pnpm add @gistui/svelte @gistui/styles @gistui/catalog
bun add @gistui/svelte @gistui/styles @gistui/catalog
```
### Solid

```sh
npm i @gistui/solid @gistui/styles @gistui/catalog
yarn add @gistui/solid @gistui/styles @gistui/catalog
pnpm add @gistui/solid @gistui/styles @gistui/catalog
bun add @gistui/solid @gistui/styles @gistui/catalog
```
### Vanilla

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

2. **Send the system prompt from your server**

   The prompt teaches the model GistUI Lang. Build it where you call the model, so it stays out of the browser bundle, and send the model's text back unchanged.

### Vercel AI SDK

```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",
    system: prompt().text,
    messages: await convertToModelMessages(messages),
  });
  return result.toTextStreamResponse();
}
```
### OpenAI (fetch)

```ts title="app/api/answer/route.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.openai.com/v1/chat/completions", {
    method: "POST",
    headers: { Authorization: `Bearer ${process.env.OPENAI_API_KEY}`, "Content-Type": "application/json" },
    body: JSON.stringify({
      model: "gpt-5",
      stream: true,
      messages: [{ role: "system", content: prompt().text }, ...messages],
    }),
  });
  if (!res.ok) return new Response(await res.text(), { status: res.status });
  return new Response(toReadable(textDeltas(res, "openai")));
}
```
### Anthropic (fetch)

```ts title="app/api/answer/route.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")));
}
```

   The fetch versions use `@gistui/server` (`npm install @gistui/server`) to turn a provider's event stream into plain text. More providers are in [Connect a model](/docs/models/).

3. **Render the answer**

   Import the stylesheet once, then give the renderer the response body.

### React

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

export function Answer() {
  const [stream, setStream] = useState<ReadableStream | null>(null);

  async function ask(text: string) {
    const res = await fetch("/api/answer", {
      method: "POST",
      body: JSON.stringify({ messages: [{ role: "user", content: text }] }),
    });
    setStream(res.body);
  }

  return (
    <>
      <button onClick={() => ask("A dashboard of last month's sales")}>Ask</button>
      {stream && <GistUI library={ui} stream={stream} onAction={(a) => a.type === "send" && ask(a.message)} />}
    </>
  );
}
```
### Vue

```vue title="Answer.vue"
<script setup lang="ts">
import "@gistui/styles/styles.css";
import { shallowRef } from "vue";
import { GistUI } from "@gistui/vue";

const stream = shallowRef<ReadableStream | null>(null);

async function ask(text: string) {
  const res = await fetch("/api/answer", {
    method: "POST",
    body: JSON.stringify({ messages: [{ role: "user", content: text }] }),
  });
  stream.value = res.body;
}
</script>

<template>
  <button @click="ask('A dashboard of last month\'s sales')">Ask</button>
  <GistUI v-if="stream" :stream="stream" @action="(a) => a.type === 'send' && ask(a.message)" />
</template>
```
### Svelte

```svelte title="Answer.svelte"
<script lang="ts">
  import "@gistui/styles/styles.css";
  import { GistUI } from "@gistui/svelte";

  let stream = $state.raw<ReadableStream | null>(null);

  async function ask(text: string) {
    const res = await fetch("/api/answer", {
      method: "POST",
      body: JSON.stringify({ messages: [{ role: "user", content: text }] }),
    });
    stream = res.body;
  }
</script>

<button onclick={() => ask("A dashboard of last month's sales")}>Ask</button>
{#if stream}
  <GistUI {stream} onaction={(a) => a.type === "send" && ask(a.message)} />
{/if}
```
### Solid

```tsx title="Answer.tsx"
import "@gistui/styles/styles.css";
import { createSignal, Show } from "solid-js";
import { GistUI } from "@gistui/solid";

export function Answer() {
  const [stream, setStream] = createSignal<ReadableStream | null>(null);

  async function ask(text: string) {
    const res = await fetch("/api/answer", {
      method: "POST",
      body: JSON.stringify({ messages: [{ role: "user", content: text }] }),
    });
    setStream(res.body);
  }

  return (
    <>
      <button onClick={() => ask("A dashboard of last month's sales")}>Ask</button>
      <Show when={stream()}>{(s) => <GistUI stream={s()} onAction={(a) => a.type === "send" && ask(a.message)} />}</Show>
    </>
  );
}
```
### Vanilla

```ts title="answer.ts"
import "@gistui/styles/styles.css";
import { mount } from "@gistui/vanilla";
import { ui } from "@gistui/vanilla/ui";

async function ask(text: string) {
  const res = await fetch("/api/answer", {
    method: "POST",
    body: JSON.stringify({ messages: [{ role: "user", content: text }] }),
  });
  mount(document.getElementById("answer")!, {
    library: ui,
    stream: res.body!,
    onAction: (a) => a.type === "send" && ask(a.message),
  });
}

ask("A dashboard of last month's sales");
```

4. **Check that it works**

   Run the app and ask for "a dashboard of last month's sales". The screen fills in while the reply streams, and pressing a follow-up suggestion sends the next question.

## If nothing renders

| What you see | Cause | Fix |
|---|---|---|
| Unstyled HTML | The stylesheet is not imported | `import "@gistui/styles/styles.css"` once, at the top of your app |
| Raw JSON events on screen, or nothing | The route sends provider events, not text | Use `textDeltas(res, format)` or the AI SDK's `toTextStreamResponse()` |
| The model answers in prose | The system prompt is not reaching the model | Pass `prompt().text` as `system` (or as the first `system` message) |
| Components look squashed by your CSS reset | Cascade layers are in the wrong order | See [Theming: with Tailwind](/docs/guides/theming/#with-tailwind) |

## Next

- [Actions and forms](/docs/guides/actions-and-forms/): what your `onAction` receives.
- [Chat](/docs/guides/chat/): replies that mix text and UI, with history.
- [Tools and live data](/docs/guides/tools-and-data/): let an answer query your API.

Source: https://gistui.com/docs/get-started/quickstart/index.mdx
