Form

The basic building block of any website or application is the form. If you don’t need a database backend, you can use the form generator to create a simple form. Ideal for contact or feedback forms where the data isn’t persisted.

Syntax

$ vela generate form [model] [fields...]
$ vela generate form contact name:text! email:email! message:editor

This will create a form at /contact and a Zod schema definition for form validation. The files created are:

src/routes/(public)/contact/+page.svelte
src/routes/(public)/contact/+page.server.ts
src/routes/(public)/contact/server.test.ts
src/lib/schemas/contact.ts

In a project with shadcn-svelte, any components the form needs are installed automatically. sveltekit-superforms and zod are installed too if the project doesn’t have them. The server test is only written when the project has the vela test:server harness (supertest installed).

Success is shown as a flash message when the project has sveltekit-flash-message. Without it, the action returns the message with superforms’ message() instead.

Options

  • --remote - Generate the form with SvelteKit remote functions instead of sveltekit-superforms
  • --route <route> - Place the form at a custom route. Defaults to the model name under (app) when auth is enabled, or (public) when it isn’t. A project with neither group gets it directly under src/routes.
  • --ui <shadcn|plain> - Generate shadcn-svelte components or plain HTML elements. Defaults to shadcn when the project has shadcn-svelte, plain otherwise.
  • --ai <description> - Design the form with AI from a natural-language description

Route

--route places the form anywhere in the route tree, including inside dynamic segments. The schema still lands in src/lib/schemas.

$ vela generate form project name:text! description:editor --route "(app)/[team_id]/projects/new"

Creates:

src/routes/(app)/[team_id]/projects/new/+page.svelte
src/routes/(app)/[team_id]/projects/new/+page.server.ts
src/routes/(app)/[team_id]/projects/new/server.test.ts
src/lib/schemas/project.ts

Keep the route you used — vela destroy form needs the same --route to find the files again.

Plain HTML

--ui defaults to shadcn when the project has a components.json and the shadcn-svelte or bits-ui package. Otherwise the form uses plain HTML elements with no classes, and no components are installed. --ui plain does the same in a project that has shadcn-svelte; --ui shadcn in a project without it is an error — set it up with npx sv add tailwindcss, then npx shadcn-svelte@latest init, or run vela bless for the full upgrade.

In a project from sv create, which has no route groups and no test harness:

$ vela generate form contact name:text! email:email! message:editor

Creates:

src/routes/contact/+page.svelte
src/routes/contact/+page.server.ts
src/lib/schemas/contact.ts

Each field’s wrapper has a data-field attribute with the field name, and data-invalid while the field has errors. Error messages have data-error. Use these to style every generated form from one stylesheet. Without sveltekit-flash-message, the success message is shown above the form.

Remote functions

--remote generates the form with SvelteKit remote functions: a form.remote.ts takes the place of +page.server.ts, and remote functions are turned on in your SvelteKit config. --route and --ui work the same way.

$ vela generate form --remote contact name:text! email:email! message:editor

Creates:

src/routes/(public)/contact/+page.svelte
src/routes/(public)/contact/form.remote.ts
src/routes/(public)/contact/server.test.ts
src/lib/schemas/contact.ts

Examples

$ vela generate form profile avatar:file bio:editor website:url
$ vela generate form feedback rating:select(1,2,3,4,5) comment:editor

AI

Pro

--ai designs the form from a natural-language description, in two stages: first the schema, then the layout. Pass a description instead of a model name and fields, not as well as — passing both is an error.

$ vela generate form --ai "a feedback form with a 1-5 star rating"

Requires an account and a linked project — run vela login and vela link first.