---
title: "Vanilla JavaScript"
description: "Mount a streamed GistUI program into any element with @gistui/vanilla - no framework, or any framework GistUI has no package for."
---

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

# Vanilla JavaScript

`@gistui/vanilla` mounts a program into any element, with the same markup, styles and behaviour as `@gistui/react`. It is the base of the Vue, Svelte and Solid packages, and it is what to use in Angular, Astro, Web Components, a plain HTML page, or anything else.

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

## Render an answer

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

const view = mount(document.getElementById("answer")!, {
  library: ui,
  stream: response.body, // a ReadableStream or async iterable of text or bytes
  onAction: (action) => console.log(action),
});
```

Or feed it a growing string. Only the appended part is parsed.

```ts
view.update({ source: textSoFar, streaming: true });
view.update({ source: fullText, streaming: false });
view.destroy();
```

`view.update()` takes any subset of the options, so it is also how you change the theme or the tools later.

> **These docs use it**
>
> Every live preview on this site is `mount()` from `@gistui/vanilla` inside an Astro page.

## Options

`stream` or `source` + `streaming`, `inline`, `onAction`, `onError`, `onStateChange`, `onProse`, `theme` (`"system"` | `"light"` | `"dark"`), `color`, `tokens`, `darkTokens`, `classNames`, `tools`, `mutations`, `onToolCall`, `allowedHosts`, `paused`, `initialState`, `lockUntil` (`"done"` or `"ready"`), `openLinks`, `autofix`, `onAutofix`.

They mean the same as in every renderer: see [Renderer options](/docs/reference/renderer-options/).

## Tools and safety

A program is untrusted: a model wrote it, possibly steered by content it read. It cannot run code.

| Option | What it does |
|---|---|
| `tools` | Read-only functions (or an MCP-style client, `{ callTool }`). `@query` may call them as soon as the UI renders, so nothing here may change anything. |
| `mutations` | Functions that change something. They run only when a person presses a button. |
| `onToolCall` | Called before every tool call with `{ name, args, kind, nodeId }`. Return `false` to block it. |
| `allowedHosts` | Hosts that images, video and backgrounds may load from, for example `["cdn.example.com", "*.example.com"]`. Set it when the model sees private data. |
| `paused` | Stops timed data refreshes. |

More in [Security](/docs/guides/security/) and [Tools and live data](/docs/guides/tools-and-data/).

## Your own components

Replace a built-in component and keep its name, or add new ones.

```ts
import { h } from "@gistui/vanilla";
import { ui } from "@gistui/vanilla/ui";

const lib = ui.extend({
  Card: (ctx) => {
    const el = h("section", { class: "my-card" });
    ctx.place(el); // the card's children go here
    return { el, update: () => true };
  },
});
```

A renderer gets `ctx` (`props`, `node`, `place`, `binding()`, `emit`, `run`, `locked`, `streaming`…) and returns `{ el, update?, destroy? }`. A form field of your own joins the Form around it with `formField(ctx, onChange)`.

Source: https://gistui.com/docs/frameworks/vanilla/index.mdx
