@gistui/vue is for Vue 3.5+ and Nuxt.
npm i @gistui/vue @gistui/styles @gistui/catalogyarn add @gistui/vue @gistui/styles @gistui/catalogpnpm add @gistui/vue @gistui/styles @gistui/catalogbun add @gistui/vue @gistui/styles @gistui/catalogRender an answer
<script setup lang="ts">
import "@gistui/styles/styles.css";
import { GistUI } from "@gistui/vue";
defineProps<{ answer: string; loading: boolean }>();
function onAction(a) {
console.log(a);
}
</script>
<template>
<GistUI :source="answer" :streaming="loading" @action="onAction" />
</template>Pass :stream="response.body" instead of source + streaming to let the component read a ReadableStream itself.
With Nuxt
Build the prompt in a server route, and render inside <ClientOnly> if the page is server-rendered.
import { prompt } from "@gistui/catalog";
import { textDeltas, toReadable } from "@gistui/server";
export default defineEventHandler(async (event) => {
const { messages } = await readBody(event);
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] }),
});
return sendStream(event, toReadable(textDeltas(res, "openai")));
});Props and events
Props: source + streaming or stream, inline, theme, color, tokens, darkTokens, classNames, tools, mutations, onToolCall, allowedHosts, paused, initialState, lockUntil, openLinks, autofix (default true), components, library.
Events: action, error, stateChange, prose, autofix.
They mean the same as in every renderer: see 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 and Tools and live data.
Your own components
Any Vue component (shadcn-vue, Reka UI, your design system) can replace a built-in one.
<script setup lang="ts">
import { GistChildren, type GistUIComponentProps } from "@gistui/vue";
defineProps<GistUIComponentProps>(); // props (what the model wrote), node, ctx
</script>
<template>
<section class="my-card"><GistChildren /></section>
</template><GistUI :source="answer" :components="{ Card: MyCard }" />Your components see the app’s plugins and whatever the components around <GistUI> provide. For a form field, useGistField(props) joins the GistUI Form around it: validation from the field’s props, error messages and bind:$var.
const field = useGistField(props); // field.name, label, required, error, locked, value, setValue