---
title: "shadcn-svelte"
description: "Render GistUI answers with your shadcn-svelte components in Svelte 5 and SvelteKit, with form validation and bind:$var still working."
---

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

# shadcn-svelte

For Svelte 5 and SvelteKit apps built with [shadcn-svelte](https://www.shadcn-svelte.com) and Tailwind.

```sh
npm i @gistui/svelte @gistui/styles @gistui/catalog
yarn add @gistui/svelte @gistui/styles @gistui/catalog
pnpm add @gistui/svelte @gistui/styles @gistui/catalog
bun add @gistui/svelte @gistui/styles @gistui/catalog
```

## 1. Order the CSS

GistUI's stylesheet must come **before** Tailwind, so the cascade layers are `theme, base, gistui, components, utilities`: above Tailwind's reset, below its utilities.

```svelte title="src/routes/+layout.svelte"
<script>
  import "@gistui/styles/styles.css";
  import "../app.css"; // @import "tailwindcss"; and the shadcn-svelte theme
  let { children } = $props();
</script>

{@render children()}
```

## 2. Match the theme with tokens

shadcn-svelte uses the same CSS variables as shadcn/ui.

```ts
export const tokens = {
  font: "inherit",
  radius: 10,
  bg: "var(--background)",
  surface: "var(--card)",
  surfaceSunk: "var(--muted)",
  surfaceHover: "var(--accent)",
  fg: "var(--foreground)",
  fgMuted: "var(--muted-foreground)",
  border: "var(--border)",
  borderStrong: "var(--input)",
  primary: "var(--primary)",
  primaryFg: "var(--primary-foreground)",
  accent: "var(--primary)",
  accentFg: "var(--primary-foreground)",
  danger: "var(--destructive)",
  chart: ["var(--chart-1)", "var(--chart-2)", "var(--chart-3)", "var(--chart-4)", "var(--chart-5)"],
};
```

## 3. Wrap components

```svelte title="GistCard.svelte"
<script lang="ts">
  import { GistChildren, type GistUIComponentProps } from "@gistui/svelte";
  import * as Card from "$lib/components/ui/card";

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

<Card.Root data-gistui="Card">
  <Card.Content class="flex flex-col gap-4 p-6"><GistChildren /></Card.Content>
</Card.Root>
```

```svelte title="GistInput.svelte"
<script lang="ts">
  import { gistField, type GistUIComponentProps } from "@gistui/svelte";
  import { Input } from "$lib/components/ui/input";
  import { Label } from "$lib/components/ui/label";

  let p: GistUIComponentProps = $props();
  const id = $props.id();
  const field = gistField(() => p.ctx); // validation, error, bind:$var
</script>

<div class="grid gap-2">
  {#if field.current.label}<Label for={id}>{field.current.label}</Label>{/if}
  <Input
    {id}
    name={field.current.name}
    type={(p.props.type as string) ?? "text"}
    placeholder={p.props.placeholder as string}
    disabled={field.current.locked}
    aria-invalid={field.current.error ? true : undefined}
    value={field.current.bound ? String(field.current.value ?? "") : (p.props.value as string)}
    oninput={(e) => field.current.bound && field.current.setValue(e.currentTarget.value)}
  />
  {#if field.current.error}<p role="alert" class="text-sm text-destructive">{field.current.error}</p>{/if}
</div>
```

Call `gistField` while the component initialises, as shown.

## 4. Render with them

```svelte
<script lang="ts">
  import { GistUI } from "@gistui/svelte";
  import GistCard from "./GistCard.svelte";
  import GistInput from "./GistInput.svelte";
  import { tokens } from "./tokens";
</script>

<GistUI source={answer} streaming={loading} components={{ Card: GistCard, Input: GistInput }} {tokens} onaction={onAction} />
```

## Notes

- Keep GistUI's built-in `Button`. With the tokens above it matches your buttons, and every action, form submit and dialog keeps working.
- For a checkbox or switch, call `gistField(() => p.ctx, "bool")`.
- shadcn-svelte is built on Bits UI: for the primitives directly, see [Bits UI](/docs/ui-libraries/bits-ui/).

Source: https://gistui.com/docs/ui-libraries/shadcn-svelte/index.mdx
