Build a Form Builder
3 minintermediatereactcodingformbuilder
Quick Answer
Create a dynamic form system with validation, conditional fields, and TypeScript type safety for form values.
Detailed Answer
Build a Form Builder
Create a dynamic form system with validation, conditional fields, and TypeScript type safety for form values.
Solution:
interface FormField {
name: string;
type: 'text' | 'email' | 'select' | 'checkbox';
label: string;
required?: boolean;
options?: string[];
condition?: (values: any) => boolean;
validation?: (value: any) => string | null;
}
interface FormBuilderProps {
fields: FormField[];
onSubmit: (values: any) => void;
}
function FormBuilder({ fields, onSubmit }: FormBuilderProps) {
const [values, setValues] = useState<Record<string, any>>({});
const [errors, setErrors] = useState<Record<string, string>>({});
const handleChange = (name: string, value: any) => {
setValues(prev => ({ ...prev, [name]: value }));
// Clear error when user starts typing
if (errors[name]) {
setErrors(prev => ({ ...prev, [name]: '' }));
}
};
const validate = () => {
const newErrors: Record<string, string> = {};
fields.forEach(field => {
const value = values[field.name];
if (field.required && (!value || value === '')) {
newErrors[field.name] = `${field.label} is required`;
}
if (field.validation && value) {
const error = field.validation(value);
if (error) newErrors[field.name] = error;
}
});
setErrors(newErrors);
return Object.keys(newErrors).length === 0;
};
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
if (validate()) {
onSubmit(values);
}
};
const visibleFields = fields.filter(field =>
!field.condition || field.condition(values)
);
return (
<form onSubmit={handleSubmit}>
{visibleFields.map(field => (
<div key={field.name}>
<label>{field.label}</label>
{field.type === 'text' || field.type === 'email' ? (
<input
type={field.type}
value={values[field.name] || ''}
onChange={(e) => handleChange(field.name, e.target.value)}
/>
) : field.type === 'select' ? (
<select
value={values[field.name] || ''}
onChange={(e) => handleChange(field.name, e.target.value)}
>
<option value="">Select...</option>
{field.options?.map(option => (
<option key={option} value={option}>{option}</option>
))}
</select>
) : (
<input
type="checkbox"
checked={values[field.name] || false}
onChange={(e) => handleChange(field.name, e.target.checked)}
/>
)}
{errors[field.name] && <span style={{ color: 'red' }}>{errors[field.name]}</span>}
</div>
))}
<button type="submit">Submit</button>
</form>
);
}
// Usage
const MyForm = () => {
const fields: FormField[] = [
{ name: 'name', type: 'text', label: 'Name', required: true },
{ name: 'email', type: 'email', label: 'Email', required: true,
validation: (value) => !/\S+@\S+\.\S+/.test(value) ? 'Invalid email' : null },
{ name: 'country', type: 'select', label: 'Country', options: ['US', 'CA', 'UK'] },
{ name: 'subscribe', type: 'checkbox', label: 'Subscribe to newsletter' },
{ name: 'phone', type: 'text', label: 'Phone',
condition: (values) => values.country === 'US' }
];
return (
<FormBuilder
fields={fields}
onSubmit={(values) => console.log(values)}
/>
);
};