Form
Formsnap and Superforms bindings for lily's Field parts.
Form wires Formsnap and Superforms to the Field parts. The visual work is all done by Field; these components
only add the form state — name, id, aria-* and error styling — so a Form.Label looks
exactly like a FieldLabel.
If you have not used Superforms and Formsnap before, read their docs first — this page assumes
you know how a superForm and its validators work.
Installation
Installs the style saved in lily.json. For a new Diamond project,
run lily-svelte init --style diamond first.
Install the lily base and `utils` (run once per project).
npx lily-svelte@latest init Install formsnap and sveltekit-superforms, then copy the form source into $lib/components/ui/form.
Anatomy
<form method="POST" use:enhance>
<Form.Field {form} name="…">
<Form.Control>
{#snippet children({ props })}
<Form.Label />
<!-- any input, spread {...props} onto it -->
{/snippet}
</Form.Control>
<Form.Description />
<Form.FieldErrors />
</Form.Field>
<Form.Button />
</form> Usage
Define the schema.
// src/routes/settings/schema.ts
import { z } from 'zod';
export const formSchema = z.object({
username: z.string().min(2).max(50)
});
export type FormSchema = typeof formSchema; Validate in load and in an action.
// src/routes/settings/+page.server.ts
import { fail } from '@sveltejs/kit';
import { superValidate } from 'sveltekit-superforms';
import { zod4 } from 'sveltekit-superforms/adapters';
import { formSchema } from './schema';
import type { Actions, PageServerLoad } from './$types.js';
export const load: PageServerLoad = async () => ({
form: await superValidate(zod4(formSchema))
});
export const actions: Actions = {
default: async (event) => {
const form = await superValidate(event, zod4(formSchema));
if (!form.valid) return fail(400, { form });
return { form };
}
}; Build the form.
<!-- src/routes/settings/settings-form.svelte -->
<script lang="ts">
import * as Form from '$lib/components/ui/form';
import { Input } from '$lib/components/ui/input';
import { superForm, type Infer, type SuperValidated } from 'sveltekit-superforms';
import { zod4Client } from 'sveltekit-superforms/adapters';
import { formSchema, type FormSchema } from './schema';
let { data }: { data: { form: SuperValidated<Infer<FormSchema>> } } = $props();
const form = superForm(data.form, { validators: zod4Client(formSchema) });
const { form: formData, enhance } = form;
</script>
<form method="POST" use:enhance>
<Form.Field {form} name="username">
<Form.Control>
{#snippet children({ props })}
<Form.Label>Username</Form.Label>
<Input {...props} bind:value={$formData.username} />
{/snippet}
</Form.Control>
<Form.Description>This is your public display name.</Form.Description>
<Form.FieldErrors />
</Form.Field>
<Form.Button>Save</Form.Button>
</form> Form.Control hands you props — name, id and the accessibility attributes — to spread on
whichever input you use. Form.Label is linked to it automatically.
Fieldset
For a group of controls that share one field — checkboxes, radios — use Form.Fieldset with Form.Legend. They render lily's FieldSet / FieldLegend, and the legend turns red with the
field's errors.