npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@chai-ui/svelte

v1.4.1

Published

Chai UI Svelte components — smart, drop-in UI for AI apps: Composer, Media Analyzer, Result Card, Edit Card, Side Panel, and the useComposer hook.

Downloads

1,516

Readme

@chai-ui/svelte

Chai UI for Svelte: drop-in components for the input and output surfaces of generative-media apps: a prompt composer, a media-to-prompt analyzer, a result card and an edit card, with the AI work built in and wired to Fal.ai, OpenRouter, Vercel AI Gateway or your own engine. Everything is controlled: you own the state, and the components render it.

These are the same components as @chai-ui/react and @chai-ui/vue, with the same behavior and the same prop names. All three are thin layers over @chai-ui/core and run one shared test suite, including the WCAG 2.2 AA checks (axe, keyboard and focus).

Building with it, or pointing a coding agent at it? Read DESIGN.md (shipped in the package) for the usage rules, and the docs site's llms.txt for the API. The code boxes on chai-ui.com/docs switch between React, Vue and Svelte.

Install

pnpm add @chai-ui/svelte @chai-ui/core @chai-ui/tokens

Svelte 5, in SvelteKit or a plain Svelte app. No Chai-specific build setup: the package ships .svelte files that your app compiles, and the components render on the server too.

Your own font

style.css loads Roboto, Material Design 3's typeface. If your app has its own font, import @chai-ui/svelte/style-without-fonts.css in its place (the same styles, without Roboto) and point the components at your font:

:root { --chai-type-family-material: "Your Font", system-ui, sans-serif; }

Dark theme

The components have a dark theme. It's opt-in: set data-chai-theme="dark" on <html>, or on any element, and every component inside it switches.

<html data-chai-theme="dark">

Chai doesn't follow the system setting on its own, because your app decides its theme. Following it, overriding a single color, and the dark values themselves are in the @chai-ui/tokens README.

Quick start

<!-- src/routes/+layout.svelte: tokens first, then the component styles that read them -->
<script lang="ts">
  import "@chai-ui/tokens/css";
  import "@chai-ui/svelte/style.css";
  import { ChaiProvider } from "@chai-ui/svelte";

  let { children } = $props();
</script>

<ChaiProvider>{@render children()}</ChaiProvider>
<!-- src/routes/+page.svelte -->
<script lang="ts">
  import { Composer, ResultCard, createFalEngine, useComposer } from "@chai-ui/svelte";

  const composer = useComposer({ engine: createFalEngine() });
  let value = $state("");
  const busy = $derived(composer.run?.results.some((r) => r.status === "queued" || r.status === "running") ?? false);

  async function enhance(prompt: string) {
    value = await composer.enhance!(prompt);
  }
</script>

<Composer
  {value}
  onChange={(next) => (value = next)}
  onSubmit={composer.submit}
  submitting={busy}
  onAbort={composer.cancel}
  onEnhance={composer.enhance && enhance}
  enhancing={composer.enhancing}
/>
{#if composer.run}
  <ResultCard results={composer.run.results} prompt={composer.run.request.prompt} onAction={() => {}} />
{/if}

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 Svelte names

The props are React's, unchanged: value and onChange, attachments and onAttachmentsChange, onSubmit, onAction, onEvent. Only these differ:

| React | Svelte | |---|---| | className, and style as an object | class, and style as a string | | useComposer / useMediaAnalyzer return values | They return an object whose fields stay current: read composer.run, don't destructure it | | Options that change between renders | Pass a function: useComposer(() => ({ engine, editImage })) | | <EditCard {...edit} /> | The same: {#if composer.edit}<EditCard {...composer.edit} />{/if} | | SidePanel's logo and footer, a Composer chip or menu item's icon | Snippets (footer also takes text) |

Call useComposer, useMediaAnalyzer and useReasoning while a component initializes (in its <script>), as with Svelte's own getContext. The use prefix is kept from React on purpose, so one name works across the docs. It marks a function you call during component setup, as Svelte libraries such as Runed and bits-ui also do, and it has nothing to do with Svelte's use: directive.

Some controls show only when you pass their callback, as in React: the optimize button with onEnhance, stop with onAbort, the Model menu with onModelChange.

Docked the bar? Set menuPlacement="above" for a bar fixed to the bottom of the screen, or "below" for one fixed to the top, so every menu opens into the page. Left out, it's "auto": each menu goes where it fits.

What's in the package

  • Composer: prompt, attachments, use cases, model and aspect-ratio menus with suggested models, prompt optimization, auto-select routing, stop and retry.
  • MediaAnalyzer + useMediaAnalyzer: media in, a generation-ready prompt out.
  • ResultCard: one prompt's results, paged, with votes, retry, download, share, details, streaming text and quality-check tags (from your backend, or the opt-in judge: useComposer({ judgeModel, judgeChecks })).
  • Analytics: onEvent on every component reports what the person does and sees, one ChaiEvent each, for any analytics tool: onEvent={(e) => posthog.capture(e.name, e.properties)}.
  • EditCard: marked regions with their own instructions, and each edit as a new version.
  • SidePanel: a panel beside your page with a Composer and its answers, for questions about the page (opt-in pageContext, shown as a removable chip) or anything else, with prompt tips, push or overlay, and either side. Same props as React's, with logo and footer as snippets. See the React README's "A side panel" for the details.
  • useComposer, ChaiProvider, and the Toggle, SearchMenu and Pagination building blocks.
  • The engines and types from @chai-ui/core, re-exported.

Your API keys stay on your server

The engines call one route on your server, which adds the key. In SvelteKit 3, declare the keys, then add the route:

// src/env.ts: the keys the route reads. Each is optional: set the ones you use.
import { defineEnvVars } from "@sveltejs/kit/env";

const optional = (value: string | undefined) => value;
export const variables = defineEnvVars({
  FAL_KEY: { schema: optional },
  OPENROUTER_API_KEY: { schema: optional },
  AI_GATEWAY_API_KEY: { schema: optional },
});
// src/routes/api/chai/[...path]/+server.ts
import { AI_GATEWAY_API_KEY, FAL_KEY, OPENROUTER_API_KEY } from "$app/env/private";
import { createChaiHandler } from "@chai-ui/core/server";
import type { RequestHandler } from "./$types";

const chai = createChaiHandler({ falKey: FAL_KEY, openRouterKey: OPENROUTER_API_KEY, aiGatewayKey: AI_GATEWAY_API_KEY });
export const fallback: RequestHandler = ({ request }) => chai.handle(request);

On SvelteKit 2, skip src/env.ts and read the keys with import { env } from "$env/dynamic/private" (env.FAL_KEY, and so on).

Then set FAL_KEY, OPENROUTER_API_KEY or AI_GATEWAY_API_KEY in your server's environment (.env works in development). Other servers, and authorize and allowedModels for production, are in the @chai-ui/core README.

Without a server route

In a desktop app (Tauri, Electron) with no server route, write an engine that hands each run to your app's backend. Pass it to useComposer({ engine: desktopEngine }) and to <ChaiProvider reasoningEngine={desktopEngine}>. The @chai-ui/core README has a Tauri example, voice input, and what Stop does there.