Form Variants
Different form layouts and patterns — switch stack and implementation mode, then pick a variant. FormConfig with React Hook Form, TanStack Form, or useActionState.
Submitted data
Waiting for a submit…
Submit the form above to see the validated payload rendered here.
Install & set up
Everything this form needs: 6 generated files to copy and 26 shared engine files the CLI can install for you.
Form stack
Which form library the generated fields are wired to. Changes the code below and the live preview above.
Implementation mode
JSON Config reads a formConfig.ts at runtime through a generic renderer. Static JSX hand-unrolls each field instead — no formConfig.ts, no runtime dispatcher.
Choose how data is fetched
Two independent choices. The default adds no dependency at all — the command below and the code both follow whatever you pick.
HTTP client — used by services.ts
Where results live — used by hooks.ts
Adds no extra packages.
Install it with the CLI
Installs every file below and its packages in one command. The steps after this are only for doing it by hand.
npx afnoui add forms/forms-job-applicationOr install the packages yourself
Only needed if you copied the files by hand instead of running the command above.
Runtime dependencies
npm install @hookform/resolvers date-fns react-hook-form zodDev dependencies
npm install -D @types/react @types/react-dom typescriptGenerated files
Generated files are yours to copy and edit; shared engine files are installed once and reused.
Generated per form(6)
Shared engine — copy once(26)
Your form's JSON configuration — defines structure, fields, validation, and layout.
import { FormConfig } from "../components/forms/types/types";
export const formConfig: FormConfig = {
"id": "job-application-form",
"title": "Senior Software Engineer",
"description": "Join our engineering team and build amazing products",
"submitLabel": "Submit Application",
"showReset": false,
"layout": "single",
"sections": [
{
"id": "personal-info",
"title": "Personal Information",
"columns": 2,
"fields": [
{
"type": "text",
"name": "firstName",
"label": "First Name",
"placeholder": "John",
"required": true
},
{
"type": "text",
"name": "lastName",
"label": "Last Name",
"placeholder": "Doe",
"required": true
},
{
"type": "email",
"name": "email",
"label": "Email",
"placeholder": "john@example.com",
"required": true
},
{
"type": "tel",
"name": "phone",
"label": "Phone",
"placeholder": "+1 (555) 000-0000"
},
{
"type": "url",
"name": "linkedin",
"label": "LinkedIn Profile",
"placeholder": "https://linkedin.com/in/johndoe"
},
{
"type": "select",
"name": "experience",
"label": "Years of Experience",
"options": [
{
"label": "0-2 years",
"value": "0-2"
},
{
"label": "3-5 years",
"value": "3-5"
},
{
"label": "5-8 years",
"value": "5-8"
},
{
"label": "8+ years",
"value": "8+"
}
]
}
]
},
{
"id": "application-details",
"title": "Application Details",
"columns": 1,
"fields": [
{
"type": "file",
"name": "resume",
"label": "Resume/CV",
"accept": ".pdf,.doc,.docx",
"required": true
},
{
"type": "textarea",
"name": "coverLetter",
"label": "Why do you want to join us?",
"placeholder": "Tell us about yourself...",
"rows": 4
}
]
}
]
};