# Svelte Usage

Use Svelte 5.29 or newer within Svelte 5. Complete the [Svelte setup](/docs/getting-started/installation/#svelte-beta)
for your host first. Send feedback through the
[Starwind UI issue tracker](https://github.com/starwind-ui/starwind-ui/issues).

These examples use `@/components/starwind`. In SvelteKit use `$lib/starwind`; Astro islands use
the configured `starwind-svelte` directory. Each component page links its generated API and
Primitive reference. Installed Svelte prop types define which props accept bindings.

## Bind Values in a Form

Use Svelte's [`$state`](https://svelte.dev/docs/svelte/$state) with a supported
[`bind:` prop](https://svelte.dev/docs/svelte/bind). `Input` supports `bind:value` and a standalone
`Checkbox` supports `bind:checked`. Native attributes such as `name`, `required`, and `type` reach
the form control. Read submitted values with `FormData`.

```svelte
<script lang="ts">
  import { Button } from "@/components/starwind/button";
  import { Checkbox } from "@/components/starwind/checkbox";
  import { Input } from "@/components/starwind/input";
  import { Label } from "@/components/starwind/label";

  let email = $state("");
  let subscribed = $state(false);
  let status = $state("");

  function submit(event: SubmitEvent & { currentTarget: HTMLFormElement }) {
    event.preventDefault();
    const data = new FormData(event.currentTarget);
    status = `Saved ${data.get("email")}. Updates: ${data.has("updates") ? "yes" : "no"}.`;
  }
</script>

<form class="grid w-full max-w-sm gap-4" onsubmit={submit}>
  <div class="grid gap-2">
    <Label for="svelte-profile-email">Email</Label>
    <Input id="svelte-profile-email" name="email" type="email" required bind:value={email} />
  </div>
  <Checkbox
    name="updates"
    value="yes"
    label="Email me product updates"
    bind:checked={subscribed}
  />
  <Button type="submit">Save preferences</Button>
  <output aria-live="polite">{status}</output>
</form>
```

`defaultValue`, `defaultChecked`, and `defaultOpen` supply initial state. A supported bound prop
receives accepted changes from the component. A later defined prop value can also set its state.
Changing the parent prop does not replay the user-change callback. For Runtime-backed models,
canceled proposals leave the bound value at its accepted state. Use the change detail's `cancel()`
method when a component documents a cancelable transition.

## Callbacks and Child Snippets

Svelte 5 components use [callback props](https://svelte.dev/docs/svelte/v5-migration-guide#Event-changes-Component-events).
Native handlers use names such as `onclick` and `onsubmit`. Starwind callbacks keep their public
names, such as `onOpenChange(nextOpen, detail)`.

This dialog keeps `open` in sync through `bind:open`. The callback can prevent closing while the
checkbox is selected. Clear it to allow the Close button, Escape key, or outside interaction to
close the dialog.

```svelte
<script lang="ts">
  import { Button } from "@/components/starwind/button";
  import { Checkbox } from "@/components/starwind/checkbox";
  import {
    Dialog,
    DialogClose,
    DialogContent,
    DialogDescription,
    DialogFooter,
    DialogHeader,
    DialogTitle,
    DialogTrigger,
  } from "@/components/starwind/dialog";

  let open = $state(false);
  let keepOpen = $state(false);
  let status = $state("Ready");
</script>

<div class="grid gap-4">
  <Dialog
    bind:open
    onOpenChange={(nextOpen, detail) => {
      if (!nextOpen && keepOpen) {
        detail.cancel();
        status = "Clear Keep this dialog open to close it.";
      }
    }}
    onCloseComplete={() => status = "Closed"}
  >
    <DialogTrigger>
      {#snippet child({ props, children })}
        <Button {...props} variant="outline">{@render children?.()}</Button>
      {/snippet}
      Edit preferences
    </DialogTrigger>
    <DialogContent>
      <DialogHeader>
        <DialogTitle>Preferences</DialogTitle>
        <DialogDescription>Choose when to allow this dialog to close.</DialogDescription>
      </DialogHeader>
      <Checkbox label="Keep this dialog open" bind:checked={keepOpen} />
      <DialogFooter>
        <DialogClose>
          {#snippet child({ props, children })}
            <Button {...props}>{@render children?.()}</Button>
          {/snippet}
          Close
        </DialogClose>
      </DialogFooter>
    </DialogContent>
  </Dialog>
  <output aria-live="polite">{open ? "Dialog open" : "Dialog closed"}. {status}</output>
</div>
```

The [`child` snippet](https://svelte.dev/docs/svelte/snippet) receives the part's `props` and
optional `children`. Spread the complete `props` object onto one compatible element or a component
that forwards it. Render `children` to keep the trigger's label. This preserves event handlers,
ARIA attributes, and attachments on the button that owns the action.

The expected element follows the part's type. For example, `DialogTrigger` uses a button, while
`HoverCardTrigger` uses an anchor. The first examples on those pages show each composition.
Keep a compound component inside one Svelte tree so its parts share context.