Loading skill
Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Build progressive forms with Conform. Use when creating forms that work without JavaScript, implementing server-validated forms in Remix/Next.js, or building accessible forms with progressive enhancement.
| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-03 | ✗→✓ | ▲ Improved | -44% | 0% |
| case-05 | ✗→✓ | ▲ Improved | 3% | 0% |
| case-11 | ✗→✓ | ▲ Improved | -28% | 0% |
| case-13 | ✗→✓ | ▲ Improved | 8% | 0% |
| case-14 | ✗→✓ | ▲ Improved | -27% | 0% |
Conform is a progressive enhancement form library. Forms work without JavaScript (server-side validation), then enhance with client-side validation when JS loads. Native to Remix and Next.js Server Actions. Uses Zod schemas for shared client/server validation.
typescript// app/actions.ts — Server action with Conform 'use server' import { parseWithZod } from '@conform-to/zod' import { z } from 'zod' const schema = z.object({ name: z.string().min(2), email: z.string().email(), message: z.string().min(10).max(1000), }) export async function submitContact(prevState: unknown, formData: FormData) { const submission = parseWithZod(formData, { schema }) if (submission.status !== 'success') { return submission.reply() // return errors to form } await db.contacts.create(submission.value) return submission.reply({ resetForm: true }) }
tsx// app/contact/page.tsx — Client form with progressive enhancement 'use client' import { useForm } from '@conform-to/react' import { parseWithZod } from '@conform-to/zod' import { useActionState } from 'react' import { submitContact } from '../actions' export default function ContactPage() { const [lastResult, action] = useActionState(submitContact, undefined) const [form, fields] = useForm({ lastResult, onValidate({ formData }) { return parseWithZod(formData, { schema }) // client-side validation }, shouldValidate: 'onBlur', shouldRevalidate: 'onInput', }) return ( <form id={form.id} onSubmit={form.onSubmit} action={action} noValidate> <div> <label htmlFor={fields.name.id}>Name</label> <input {...getInputProps(fields.name, { type: 'text' })} /> <p>{fields.name.errors}</p> </div> <div> <label htmlFor={fields.email.id}>Email</label> <input {...getInputProps(fields.email, { type: 'email' })} /> <p>{fields.email.errors}</p> </div> <div> <label htmlFor={fields.message.id}>Message</label> <textarea {...getTextareaProps(fields.message)} /> <p>{fields.message.errors}</p> </div> <button type="submit">Send</button> </form> ) }
shouldValidate: 'onBlur' validates when user leaves field — less aggressive than onChange.Other measured skills in the registry, with their headline benchmark lift.