easy-forms

Quick start

Build your first validated, accessible form in about five minutes.

This guide takes you from an empty file to a working, validated form. It assumes you have completed Installation.

Describe your form as data

A schema is an object with a groups array. Each group holds questions. Each question has a key, a label, and a control.

signup-form.tsx
import type { FormSchema } from '@easy-forms/core';

const schema: FormSchema = {
	title: 'Create your account',
	groups: [
		{
			layout: 'grid',
			gridCols: 2,
			questions: [
				{ key: 'firstName', label: 'First name', control: 'text', validators: { required: true, minLength: 2 } },
				{ key: 'lastName', label: 'Last name', control: 'text', validators: { required: true } },
			],
		},
		{
			questions: [
				{ key: 'email', label: 'Email', control: 'email', placeholder: 'you@example.com', validators: { required: true, email: true } },
				{ key: 'plan', label: 'Plan', control: 'dropdown', placeholder: 'Choose a plan', options: [
					{ value: 'free', label: 'Free' },
					{ value: 'pro', label: 'Pro' },
				], validators: { required: true } },
			],
		},
	],
};

Render it with <EasyForm>

Pass the schema, optional initialValues, and an onSubmit handler. <EasyForm> — added to your repo by shadcn add @easy-forms/easy-form — wires the renderer registry and form chrome for you, so there's no registry prop.

signup-form.tsx
import { EasyForm } from '@/components/easy-forms/easy-form';

export function SignupForm() {
	return (
		<EasyForm
			schema={schema}
			initialValues={{ firstName: '', lastName: '', email: '', plan: null }}
			onSubmit={async (values) => {
				await fetch('/api/signup', { method: 'POST', body: JSON.stringify(values) });
			}}
		/>
	);
}

Prefer to keep the lower-level component or bring your own UI? Use <Form> from @easy-forms/core with a registry prop — <EasyForm> is just a thin wrapper around it.

That's it — here it is, live

The submit button stays disabled until the form is dirty and valid. Try submitting with empty fields, then fill them in.

Preview
Live

Create your account

Type your form data

Pass a type argument to EasyForm (and FormSchema) to get autocomplete and type-checking on field keys, derived values, and dependency sources.

interface SignupData extends Record<string, unknown> {
	firstName: string;
	lastName: string;
	email: string;
	plan: string | null;
}

const schema: FormSchema<SignupData> = {
	/* keys are now checked against SignupData */
	groups: [],
};

<EasyForm<SignupData> schema={schema} onSubmit={(values) => {
	// values is typed as SignupData
}} />

Next steps