The Future of Web Dev
The Future of Web Dev
Schema-First Form Generator for Svelte – autoform-svelte
Generate schema-driven forms in Svelte with autoform-svelte. Use zodAdapter, field metadata, and shadcn-svelte for styled output.

autoform-svelte is a schema-first form generator for Svelte that creates complete, interactive forms directly from a JSON Schema or Zod schema definition.
It reads your schema at runtime, maps each field type to the correct HTML control, and binds form state through Svelte 5’s $state reactive primitive. No manual field wiring is needed.
Features
๐ Adapter-Based Schema Support: Ships with zodAdapter for Zod v4 and jsonSchemaAdapter for plain JSON Schema.
๐๏ธ Field Metadata API: Attach a .meta({ form: { ... } }) block to any Zod field to override the label, placeholder, widget type, or object key ordering.
๐จ Theming System: The native HTML theme works without configuration. The shadcn-svelte theme wires your existing shadcn component imports into the renderer through createShadcnTheme().
๐ Global Theme Provider: Wrap multiple <Autoform> instances in <AutoformProvider> to apply a shared theme and adapter across all forms.
๐ Zod Type Coverage: Renders text inputs, number inputs, checkboxes, select dropdowns, grouped object fields, arrays with add/remove controls, discriminated unions as variant switchers, and datetime pickers.
๐ Dynamic Key-Value Fields: .catchall() and z.record() types render as editable key-value pair lists.
โ
Event-Based Validation: Fires a submit event with bound data on success, an error event with a string array on validation failure, and a cancel event on dismissal.
๐ข Bindable Data Object: The data prop binds two-way to a $state variable.
Use Cases
- Admin Panels: Generate forms for managing resources where schemas already exist, such as content models or user profiles.
- Settings Pages: Build configuration interfaces from JSON schemas that define available options and defaults.
- CRUD Workflows: Create, update, and delete forms driven by backend data models expressed as schemas.
- Dynamic Forms: Render forms based on userโselected schemas, for example in form builders or survey tools.
How to Use It
Table Of Contents
Installation
Install the core package with your package manager.
bun install autoform-svelteIf your project uses Zod schemas, install Zod separately.
bun install zodBasic Usage with JSON Schema
The <Autoform> component accepts a raw JSON Schema object. The jsonSchemaAdapter is the default, so no adapter prop is required.
<script lang="ts">
import { Autoform } from "autoform-svelte";
const schema = {
type: "object",
properties: {
username: { type: "string" },
age: { type: "number" },
active: { type: "boolean" },
},
required: ["username"],
};
let data = $state({ username: "", age: 0, active: false });
</script>
<Autoform
{schema}
bind:data
onsubmit={(e) => console.log("submitted", e.detail.data)}
oncancel={() => console.log("cancelled")}
/>Usage with Zod
Import zodAdapter and pass it to the adapter prop. Define your schema as a standard Zod object.
<script lang="ts">
import { Autoform } from "autoform-svelte";
import { zodAdapter } from "autoform-svelte/adapters/zod";
import { z } from "zod";
const schema = z.object({
email: z.string().email(),
subscribe: z.boolean().default(false),
role: z.enum(["admin", "editor", "viewer"]),
});
let data = $state({
email: "",
subscribe: false,
role: "viewer",
});
</script>
<Autoform
{schema}
adapter={zodAdapter}
bind:data
title="User Settings"
submitLabel="Save Settings"
onsubmit={(e) => console.log("saved", e.detail.data)}
onerror={(e) => console.error("errors", e.detail.errors)}
/>Custom Adapter
Use createCustomAdapter() when your schema library can serialize its schema to a plain JSON-compatible object.
import { createCustomAdapter } from "autoform-svelte";
const myAdapter = createCustomAdapter((schema) => schema.toJSON());Pass myAdapter to the adapter prop on any <Autoform> instance.
Field Metadata
Attach a .meta() block to individual Zod fields to control widget type, label text, and placeholder copy.
import { z } from "zod";
const schema = z.object({
bio: z.string().meta({
form: {
label: "About You",
widget: "textarea",
placeholder: "Write a short bio",
},
}),
age: z.number().meta({
form: {
label: "Your Age",
placeholder: "e.g. 28",
},
}),
});To allow reordering of object keys in the rendered form, set reorderable: true on the object type.
const schema = z.object({
title: z.string(),
body: z.string(),
}).meta({
form: {
reorderable: true,
},
});Theming: Native HTML (Default)
The native theme renders plain HTML elements with no additional setup. Pass only the schema and event handlers.
<Autoform schema={jsonSchema} onsubmit={handleSubmit} />Theming: shadcn-svelte
Create a theme file that imports your shadcn-svelte components and passes them to createShadcnTheme().
// src/lib/theme.ts
import { createShadcnTheme } from "autoform-svelte/themes/shadcn";
import { Button } from "$lib/components/ui/button";
import { Input } from "$lib/components/ui/input";
import { Textarea } from "$lib/components/ui/textarea";
import { Checkbox } from "$lib/components/ui/checkbox";
import { Label } from "$lib/components/ui/label";
import * as Select from "$lib/components/ui/select";
import * as Card from "$lib/components/ui/card";
import * as Field from "$lib/components/ui/field";
export const shadcnTheme = createShadcnTheme({
Button,
Input,
Textarea,
Checkbox,
Label,
Field,
Select,
Card,
});Pass the theme to a single form instance with the theme prop.
<script lang="ts">
import { Autoform } from "autoform-svelte";
import { zodAdapter } from "autoform-svelte/adapters/zod";
import { shadcnTheme } from "$lib/theme";
import { z } from "zod";
const schema = z.object({
name: z.string().min(1),
notes: z.string().optional(),
});
let data = $state({ name: "", notes: "" });
</script>
<Autoform
{schema}
adapter={zodAdapter}
theme={shadcnTheme}
bind:data
onsubmit={(e) => console.log(e.detail.data)}
/>Global Theme with AutoformProvider
Wrap multiple forms in <AutoformProvider> to set a shared theme and adapter at the top level. Each child <Autoform> inherits the provider’s configuration.
<script lang="ts">
import { Autoform, AutoformProvider } from "autoform-svelte";
import { zodAdapter } from "autoform-svelte/adapters/zod";
import { shadcnTheme } from "$lib/theme";
import { userSchema, settingsSchema } from "$lib/schemas";
let userData = $state({});
let settingsData = $state({});
</script>
<AutoformProvider theme={shadcnTheme} adapter={zodAdapter}>
<Autoform schema={userSchema} bind:data={userData} onsubmit={handleUser} />
<Autoform schema={settingsSchema} bind:data={settingsData} onsubmit={handleSettings} />
</AutoformProvider>API Reference
Props
| Prop | Type | Default | Description |
|---|---|---|---|
schema | unknown | required | The schema object the adapter parses |
adapter | SchemaAdapter | jsonSchemaAdapter | Schema-to-field-tree converter |
data | Record<string, any> | {} | Bindable two-way form data object |
title | string | โ | Optional heading rendered above the form |
submitLabel | string | "Save" | Text on the submit button |
cancelLabel | string | "Cancel" | Text on the cancel button |
theme | AutoformTheme | nativeTheme | UI component theme |
Events
| Event | Payload | Description |
|---|---|---|
submit | { data } | Fires after the form passes validation |
cancel | โ | Fires when the cancel button is clicked |
error | { errors: string[] } | Fires when validation fails; carries an array of error strings |
Supported Zod Types
| Zod Type | Rendered Control |
|---|---|
z.string() | Text input (default); textarea via form.widget metadata |
z.number() | Number input |
z.boolean() | Checkbox |
z.enum([...]) | Select dropdown |
z.object({...}) | Grouped field section |
z.array(...) | List with add and remove controls |
z.discriminatedUnion(...) | Variant switcher |
z.string().datetime() | Date/time picker |
.catchall() / z.record() | Editable key-value pair list |
Field Metadata Keys
| Key | Type | Default | Description |
|---|---|---|---|
form.widget | "input" | "textarea" | "input" | Widget override for string fields |
form.label | string | โ | Overrides the auto-generated field label |
form.placeholder | string | โ | Placeholder text for input, textarea, select, date, and number controls |
form.reorderable | boolean | false | Adds key reordering controls to object fields |
Related Resources
- Svelte: The component framework that autoform-svelte targets.
- Zod: TypeScript-first schema validation library.
- shadcn-svelte: Svelte port of shadcn/ui.
- JSON Schema: Open standard for describing data structures.
- 10 Best Form Builder Plugins To Generate Forms From JSON Data
FAQs
Q: How do I handle a field that is conditionally required?
A: Use Zod’s .superRefine() or a z.discriminatedUnion() at the schema level. The discriminated union type renders as a variant switcher, so the form shows only the fields relevant to the active variant.
Q: Can I add custom field components outside of the shadcn-svelte theme?
A: Use createCustomAdapter() to intercept schema nodes, and build your own theme object that maps field types to your component implementations. The theming API is not restricted to shadcn-svelte; any Svelte component set works as long as it conforms to the AutoformTheme interface.
Q: What happens when validation fails?
A: The form fires an error event with { errors: string[] }. Each string in the array corresponds to a failed validation rule. No submit event fires until all validation rules pass.
Q: Is <AutoformProvider> required for a single form?
A: No. Pass theme and adapter directly to the <Autoform> component for single-form usage. <AutoformProvider> exists only to avoid repeating those props across multiple form instances in the same component tree.





