FrontendAdvanced
18 min readNov 24, 2025

Form Architecture for Large Frontend Apps

Build robust form systems with validation, schemas, and reusable logic for enterprise applications.

R

Rithy Tep

Author

Form Architecture for Large Frontend Apps

Schema-Based Validation with Zod

import { z } from 'zod' const userSchema = z.object({ email: z.string().email('Invalid email'), password: z.string().min(8, 'Password must be 8+ characters'), role: z.enum(['admin', 'user']), profile: z.object({ firstName: z.string().min(1), lastName: z.string().min(1), age: z.number().min(18).optional() }) }) type UserForm = z.infer<typeof userSchema>

React Hook Form Integration

import { useForm } from 'react-hook-form' import { zodResolver } from '@hookform/resolvers/zod' export function UserForm() { const { register, handleSubmit, formState: { errors, isSubmitting } } = useForm<UserForm>({ resolver: zodResolver(userSchema) }) const onSubmit = async (data: UserForm) => { await fetch('/api/users', { method: 'POST', body: JSON.stringify(data) }) } return ( <form onSubmit={handleSubmit(onSubmit)}> <input {...register('email')} /> {errors.email && <span>{errors.email.message}</span>} <input type="password" {...register('password')} /> {errors.password && <span>{errors.password.message}</span>} <button disabled={isSubmitting}>Submit</button> </form> ) }

Dynamic Forms

interface FieldConfig { name: string type: 'text' | 'email' | 'select' | 'number' label: string required?: boolean options?: { value: string; label: string }[] } function DynamicForm({ fields }: { fields: FieldConfig[] }) { const { register, handleSubmit } = useForm() return ( <form onSubmit={handleSubmit(console.log)}> {fields.map(field => ( <div key={field.name}> <label>{field.label}</label> {field.type === 'select' ? ( <select {...register(field.name, { required: field.required })}> {field.options?.map(opt => ( <option key={opt.value} value={opt.value}> {opt.label} </option> ))} </select> ) : ( <input type={field.type} {...register(field.name, { required: field.required })} /> )} </div> ))} </form> ) }

Reusable Form Logic

// composables/useFormSubmit.ts export function useFormSubmit<T>( endpoint: string, onSuccess?: (data: T) => void ) { const [loading, setLoading] = useState(false) const [error, setError] = useState<string | null>(null) const submit = async (data: T) => { setLoading(true) setError(null) try { const response = await fetch(endpoint, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data) }) if (!response.ok) throw new Error('Submission failed') const result = await response.json() onSuccess?.(result) return result } catch (err) { setError(err.message) throw err } finally { setLoading(false) } } return { submit, loading, error } }

Conclusion

Schema-based validation with TypeScript ensures type-safe forms. React Hook Form and Zod make complex validation simple and maintainable.

#Forms#Validation#React Hook Form#Zod#Vue
Form Architecture for Large Frontend Apps | Rithy Tep | Mercy Dev