For Svelte 5 apps that use Bits UI primitives directly. If you use shadcn-svelte, which is built on Bits UI, start with shadcn-svelte.
npm i @gistui/svelte @gistui/styles @gistui/catalog bits-uiyarn add @gistui/svelte @gistui/styles @gistui/catalog bits-uipnpm add @gistui/svelte @gistui/styles @gistui/catalog bits-uibun add @gistui/svelte @gistui/styles @gistui/catalog bits-uiA switch
Bits UI form primitives render a hidden native input when given name, so the GistUI Form around them can read the value.
<script lang="ts">
import { Switch } from "bits-ui";
import { gistField, type GistUIComponentProps } from "@gistui/svelte";
let p: GistUIComponentProps = $props();
const id = $props.id();
const field = gistField(() => p.ctx, "bool"); // one on/off value
</script>
<div class="switch-row">
<Switch.Root
{id}
name={field.current.name}
disabled={field.current.locked}
checked={field.current.bound ? field.current.value === true : p.props.checked === true}
onCheckedChange={(on) => field.current.bound && field.current.setValue(on)}
class="switch"
>
<Switch.Thumb class="switch-thumb" />
</Switch.Root>
<label for={id}>{field.current.label}</label>
</div>A card
<script lang="ts">
import { GistChildren, type GistUIComponentProps } from "@gistui/svelte";
let {}: GistUIComponentProps = $props();
</script>
<section class="my-card" data-gistui="Card"><GistChildren /></section>Register them
<script lang="ts">
import { GistUI } from "@gistui/svelte";
import GistCard from "./GistCard.svelte";
import GistSwitch from "./GistSwitch.svelte";
</script>
<GistUI source={answer} streaming={loading} components={{ Switch: GistSwitch, Card: GistCard }} onaction={onAction} />Notes
- Call
gistFieldwhile the component initialises, and read it throughfield.current. - Keep GistUI’s built-in
Buttonand style it with tokens: actions, form submits and dialogs then keep working. - Bits UI ships no styles. Use tokens so the GistUI components you did not replace match yours.