Form Generator Component
Overview
The Form Generator is a sophisticated, enterprise-grade React component designed to streamline the creation and management of complex forms. It combines the power of declarative configuration with the flexibility of runtime customization, making it an ideal solution for applications requiring dynamic form generation.
Key Features
- Schema-driven form generation
- Built-in validation with Yup integration
- Advanced conditional rendering system
- Extensible field type registry
- Responsive layout management
- Performance-optimized rendering
- Comprehensive TypeScript support
Philosophy
The Form Generator is built on three core principles:
-
Declarative Over Imperative
- Forms should be described, not programmed
- Configuration should be clear and self-documenting
- Complex behaviors should emerge from simple rules
-
Type Safety First
- Full TypeScript support throughout the component
- Compile-time validation of form configurations
- Runtime type checking for dynamic data
-
Progressive Enhancement
- Simple forms should be simple to create
- Complex features should be opt-in
- Advanced functionality shouldn't compromise basic usage
Core Features
1. Schema-Based Form Generation
interface FormSchema {
fields: FieldDefinition[];
validation?: ValidationSchema;
layout?: LayoutConfiguration;
defaultValues?: Record<string, any>;
onSubmit?: (values: any) => Promise<void>;
}
const userFormSchema: FormSchema = {
fields: [
{
name: 'email',
type: 'email',
label: 'Email Address',
required: true,
validation: yup.string().email()
},
{
name: 'preferences',
type: 'fieldset',
fields: [
{
name: 'notifications',
type: 'switch',
label: 'Enable Notifications'
}
]
}
]
};
2. Advanced Field Types
type FieldType =
| 'text'
| 'number'
| 'email'
| 'password'
| 'select'
| 'multiselect'
| 'radio'
| 'checkbox'
| 'switch'
| 'date'
| 'time'
| 'datetime'
| 'fieldset'
| 'custom';
interface CustomField {
component: React.ComponentType<FieldProps>;
defaultValue: any;
validation?: ValidationRule;
}
FieldRegistry.register('signature-pad', {
component: SignaturePadField,
defaultValue: null,
validation: yup.mixed()
});
Getting Started
Installation
npm install @wrkbelt/ui-components
npm install yup @hookform/resolvers/yup
Basic Usage
import { FormGenerator } from '@wrkbelt/ui-components';
import * as yup from 'yup';
const SimpleForm = () => {
const config = {
fields: [
{
name: 'username',
type: 'text',
label: 'Username',
required: true,
hint: 'Choose a unique username'
},
{
name: 'email',
type: 'email',
label: 'Email Address',
validation: yup.string().email()
}
],
onSubmit: async (values) => {
await saveUser(values);
}
};
return <FormGenerator config={config} />;
};
Architecture
The Form Generator follows a modular architecture designed for extensibility and maintainability:
.
├── components - Small Components that make up our Form Generator
│ ├── conditional-renderer.tsx - Responsible for rendering conditional fields
│ ├── fields - Fields are the building blocks of our Form Generator
│ │ ├── ...
├── contexts
│ └── form-generator-context.tsx - Context that holds the Form State & Configuration
├── form-generator.tsx
├── types
│ ├── form.ts - Types for our external Form Generator API
│ └── schema.ts - Types for the internal Form Generator parts
└── ...
State Management
The Form Generator leverages React Hook Form for robust form state management through our FormGeneratorProvider context. This provider encapsulates:
- Form state and methods from React Hook Form
- Global form disabled state
- Form identification
interface FormGeneratorContextValue<TFieldValues extends FieldValues> {
form: UseFormReturn<TFieldValues>;
disabled: boolean;
formId: string;
}
The context is automatically set up when using the FormGenerator component, giving you access to all React Hook Form features including validation, field state tracking, and form submission handling.
Validation System
The Form Generator implements a comprehensive validation system:
1. Schema Validation
const validationSchema = {
username: yup
.string()
.required('Username is required')
.min(3, 'Username must be at least 3 characters')
.matches(/^[a-zA-Z0-9_]+$/, 'Username can only contain letters, numbers, and underscores'),
email: yup
.string()
.required('Email is required')
.email('Invalid email format'),
password: yup
.string()
.required('Password is required')
.min(8, 'Password must be at least 8 characters')
.matches(
/^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d]{8,}$/,
'Password must contain at least one letter and one number'
)
};
2. Field-Level Validation
const fields = [
{
name: 'age',
type: 'number',
validation: {
min: { value: 18, message: 'Must be at least 18 years old' },
max: { value: 100, message: 'Invalid age' }
}
},
{
name: 'website',
type: 'text',
validation: {
pattern: {
value: /^https?:\/\/.+\..+$/,
message: 'Must be a valid URL'
}
}
}
];
Conditional Logic
Fields and fieldsets can be conditionally rendered based on form values using the showIf property:
const fields = [
{
name: 'requestType',
type: 'select',
label: 'Request Type',
options: [
{ label: 'Bug Report', value: 'bug' },
{ label: 'Feature Request', value: 'feature' }
]
},
{
name: 'severity',
type: 'select',
label: 'Severity',
showIf: (values) => values.requestType === 'bug',
options: [
{ label: 'Low', value: 'low' },
{ label: 'Medium', value: 'medium' },
{ label: 'High', value: 'high' }
]
}
];
Best Practices
1. Field Organization
// Group related fields in fieldsets
const addressFields = {
type: 'fieldset',
label: 'Address Information',
fields: [
{
name: 'street',
type: 'text',
label: 'Street Address'
},
{
name: 'city',
type: 'text',
label: 'City'
},
{
name: 'country',
type: 'select',
label: 'Country',
options: countries
}
]
};
2. Error Handling
// Implement comprehensive error handling
const handleSubmit = async (values) => {
try {
await submitForm(values);
} catch (error) {
if (error instanceof ValidationError) {
// Handle validation errors
return {
success: false,
errors: error.details
};
}
if (error instanceof NetworkError) {
// Handle network errors
return {
success: false,
message: 'Network error occurred'
};
}
// Handle unexpected errors
return {
success: false,
message: 'An unexpected error occurred'
};
}
};
Troubleshooting
Common Issues
- Validation Not Triggering
// Incorrect
const field = {
name: 'email',
validation: 'email' // String won't work
};
// Correct
const field = {
name: 'email',
validation: yup.string().email('Invalid email format')
};