A design system for AI model interactions. Every component comes with its use case instructions built in, is built on Material Design 3 and styled by your design tokens, and runs on Fal.ai, OpenRouter, Vercel AI Gateway or your own engine. For React and Vue.
Docs and live examples: chai-ui.com/docs
Building with an AI agent? Point it at DESIGN.md (it also ships in both npm packages) and the full reference at chai-ui.com/docs/llms.txt, then ask it what Chai UI can do. Between them they cover every component, use case and option.
| Component | What it does |
|---|---|
Composer |
A prompt bar for image, video, audio, text and world models, with attachments, use cases, a pre-filled model menu whose aspect ratios match each model, prompt optimization, auto-select routing, stop and opt-in voice input. |
MediaAnalyzer |
Turns a dropped image, video or audio clip into a generation-ready prompt, with the analysis model and prompt length chosen by the user. |
ResultCard |
Shows one prompt's results from one or several models in a single card, paging between them, as image, video, audio or streaming text with like, retry, download, share, a details flip and pass or fail quality checks. |
EditCard |
Edits an image through regions drawn on it, each with its own instruction, then pages from the original through every edited version. |
pnpm add @chai-ui/react @chai-ui/core @chai-ui/tokens # React 18.3 or 19
pnpm add @chai-ui/vue @chai-ui/core @chai-ui/tokens # Vue 3.5 or later, Nuxt includedBoth packages have the same components and behavior, and run the same tests.
import "@chai-ui/tokens/css";
import "@chai-ui/react/style.css";
import { useState } from "react";
import { ChaiProvider, Composer, ResultCard, createFalEngine, useComposer } from "@chai-ui/react";
const engine = createFalEngine();
function App() {
const [value, setValue] = useState("");
const { run, submit, cancel } = useComposer({ engine });
const busy = run?.results.some((r) => r.status === "queued" || r.status === "running") ?? false;
return (
<>
<Composer value={value} onChange={setValue} onSubmit={submit} submitting={busy} onAbort={cancel} />
{run && <ResultCard results={run.results} prompt={run.request.prompt} onAction={() => {}} />}
</>
);
}
export default function Root() {
return (
<ChaiProvider>
<App />
</ChaiProvider>
);
}The same in Vue
<!-- main.ts imports "@chai-ui/tokens/css" and "@chai-ui/vue/style.css";
ChaiProvider wraps this component, e.g. in App.vue. -->
<script setup lang="ts">
import { computed, ref } from "vue";
import { Composer, ResultCard, createFalEngine, useComposer } from "@chai-ui/vue";
const engine = createFalEngine();
const value = ref("");
const { run, submit, cancel } = useComposer({ engine });
const busy = computed(() => run.value?.results.some((r) => r.status === "queued" || r.status === "running") ?? false);
</script>
<template>
<Composer v-model="value" :submitting="busy" @submit="submit" @abort="cancel" />
<ResultCard v-if="run" :results="run.results" :prompt="run.request.prompt" @action="() => {}" />
</template>To try it before setting up a provider, leave out engine and
ChaiProvider: everything then runs on built-in mocks, with no network
calls and no keys.
The engines call one route on your server, which adds the key. In Next.js it's one file:
// app/api/chai/[...path]/route.ts
import { createChaiHandler } from "@chai-ui/core/server";
export const { GET, POST, PUT } = createChaiHandler();Then set FAL_KEY, OPENROUTER_API_KEY or AI_GATEWAY_API_KEY on your server. React Router
(or Remix), Nuxt, and API servers behind a Vite app (Express, Hono,
Cloudflare Workers, Bun, Deno), are covered in the @chai-ui/core README,
along with authorize and allowedModels for production.
| Package | What it is |
|---|---|
@chai-ui/react |
The components and hooks, for React |
@chai-ui/vue |
The same components and composables, for Vue |
@chai-ui/core |
Framework-free data model, engines and the server route |
@chai-ui/tokens |
Design tokens as CSS variables, JS and a Figma token set |
Chai's components are built to support WCAG 2.2 AA. Every component is tested in a real browser with axe, against the WCAG 2.0, 2.1 and 2.2 A and AA rules, and with keyboard and focus checks, in both the React and Vue packages. Automated checks can't catch everything, and conformance belongs to a whole page, so your app still needs its own checks. See DESIGN.md.
- Questions and troubleshooting: SUPPORT.md
- Bugs and ideas: open an issue
- Contributing: CONTRIBUTING.md
- Security: SECURITY.md
MIT © kazarazat
Illustrations © 2026 Imani Razat, used with permission. They aren't covered by the MIT license.

