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.
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.
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.
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
}} />