@nestledjs/forms
Version:
A flexible React form library supporting both declarative and imperative usage patterns with TypeScript support
793 lines (677 loc) β’ 20.7 kB
Markdown
# Forms Library Enhancement Roadmap
## Path to Building the Most Powerful Universal Form Library
This document analyzes the current state of our forms library and outlines a roadmap for evolving it into the most powerful universal form library available.
pnpm
## Current State Assessment π―
### What We're Doing Exceptionally Well
#### 1. **Type Safety Architecture**
Our discriminated union system with `FormField` types provides perfect IntelliSense and prevents runtime errors. This is better than most form libraries.
```typescript
// Our excellent type safety approach
FormFieldProps<Extract<FormField, { type: FormFieldType.YourNewField }>>
```
#### 2. **DRY Component Architecture**
The refactored search select base component pattern eliminates massive code duplication that plagues most libraries:
- 80%+ code reduction in search select variants
- Single source of truth for common patterns
- Consistent behavior across all variants
#### 3. **Theme System**
The inheritance system where global styles flow down to field-specific themes is sophisticated:
- Zod schema validation for themes
- Type-safe theme merging
- Consistent styling patterns
- Easy customization without breaking changes
#### 4. **Developer Experience**
- Factory pattern with `FormFieldClass` is intuitive
- Comprehensive documentation and creation guide
- Both declarative and imperative APIs
- Excellent Storybook integration
- Clear separation of concerns
#### 5. **Read-Only Patterns**
The dual read-only modes (`'value'` vs `'disabled'`) show deep understanding of real-world needs.
## Enhancement Opportunities π
### 1. **Advanced Validation System**
**Current State:**
```typescript
validate?: (value: any) => string | boolean
```
**Enhanced Vision:**
```typescript
validation?: {
schema?: ZodSchema | YupSchema | JoiSchema
rules?: ValidationRule[]
crossField?: (formValues: any) => ValidationResult
async?: AsyncValidator
debounce?: number
showOn?: 'blur' | 'change' | 'submit'
dependencies?: string[] // Other fields that affect this validation
}
// Example usage
FormFieldClass.email('email', {
label: 'Email',
validation: {
schema: z.string().email(),
async: {
validator: checkEmailAvailability,
debounce: 500,
message: 'Checking availability...'
},
crossField: (values) =>
values.confirmEmail === values.email || 'Emails must match'
}
})
```
### 2. **Conditional Field Logic** β
**IMPLEMENTED**
**COMPLETED:** Dynamic field behavior based on form values.
**Implementation Details:**
- Added `showWhen`, `requiredWhen`, and `disabledWhen` functions to `BaseFieldOptions`
- Implemented in `RenderFormField` component for universal compatibility
- Works with both declarative and imperative APIs
- Performance optimized with `useMemo` and reactive form watching
- Robust error handling for conditional functions
- Dynamic validation updates when conditions change
**Usage:**
```typescript
FormFieldClass.text('email', {
label: 'Email Address',
showWhen: (formValues) => formValues.contactMethod === 'email',
requiredWhen: (formValues) => formValues.accountType === 'business',
disabledWhen: (formValues) => formValues.useCompanyEmail === true,
})
// Complex nested conditions
FormFieldClass.text('officeNumber', {
label: 'Office Number',
showWhen: (values) => values.userType === 'teacher' && values.isHeadOfDepartment,
requiredWhen: (values) => values.userType === 'teacher' && values.isHeadOfDepartment,
})
```
**Benefits Achieved:**
- β
Works with ALL existing field types immediately
- β
No breaking changes to existing API
- β
Consistent behavior across declarative/imperative usage
- β
Dynamic validation that updates in real-time
- β
Performance optimized with minimal re-renders
- β
Comprehensive Storybook documentation and examples
**Future Enhancement Ideas:**
```typescript
// Advanced declarative conditions (potential future feature)
conditions: {
show: [
{ field: 'contactMethod', operator: 'equals', value: 'email' },
{ field: 'hasEmail', operator: 'equals', value: true }
],
required: [
{ field: 'accountType', operator: 'in', value: ['business', 'enterprise'] }
]
}
// Multi-step form support (potential future feature)
FormFieldClass.group('personalInfo', {
label: 'Personal Information',
showWhen: (formValues, step) => step === 1,
fields: [
FormFieldClass.text('firstName', { label: 'First Name' }),
FormFieldClass.text('lastName', { label: 'Last Name' })
]
})
```
### 3. **Advanced Layout System**
```typescript
FormFieldClass.text('firstName', {
label: 'First Name',
layout: {
// Responsive grid system
grid: {
xs: 12, // Full width on mobile
md: 6, // Half width on tablet
lg: 4 // Third width on desktop
},
// Flexbox properties
flex: {
grow: 1,
shrink: 0,
basis: 'auto'
},
// CSS Grid properties
gridArea: 'firstName',
alignSelf: 'center'
}
})
// Advanced layout containers
<Form
id="complex-form"
layout={{
type: 'grid',
columns: { xs: 1, md: 2, lg: 3 },
gap: '1rem',
areas: [
['firstName', 'lastName', 'email'],
['address', 'address', 'phone'],
['notes', 'notes', 'submit']
]
}}
fields={formFields}
submit={handleSubmit}
/>
```
### 4. **State Management Enhancements**
```typescript
<Form
id="user-form"
// Auto-save functionality
autoSave={{
interval: 30000, // 30 seconds
endpoint: '/api/drafts',
onSave: (data) => console.log('Draft saved', data),
onError: (error) => console.error('Save failed', error)
}}
// Persistence across browser sessions
persistence={{
key: 'user-draft-123',
storage: 'localStorage', // or 'sessionStorage' or 'indexedDB'
encrypt: true, // For sensitive data
expiry: 24 * 60 * 60 * 1000 // 24 hours
}}
// Undo/Redo functionality
undoRedo={{
enabled: true,
maxHistorySize: 50,
ignoreFields: ['timestamp'], // Fields to exclude from history
debounce: 1000 // Group rapid changes
}}
// Optimistic updates for real-time sync
optimisticUpdates={{
enabled: true,
syncEndpoint: '/api/forms/sync',
conflictResolution: 'last-writer-wins' // or 'merge' or 'prompt-user'
}}
/>
```
### 5. **Performance Optimizations**
```typescript
// Lazy loading for heavy components
const LazyRichTextEditor = lazy(() => import('./rich-text-field'))
const LazyFileUploader = lazy(() => import('./file-upload-field'))
// Virtual scrolling for massive forms
<Form
id="large-form"
virtualScroll={{
enabled: true,
windowSize: 20,
bufferSize: 5,
estimatedItemHeight: 80
}}
fields={thousandsOfFields}
/>
// Field-level lazy loading
FormFieldClass.richText('description', {
label: 'Description',
lazy: {
threshold: 'visible', // Load when scrolled into view
placeholder: <Skeleton height={100} />
}
})
// Memoization and optimization
FormFieldClass.select('country', {
label: 'Country',
options: countries,
optimize: {
memoize: true,
virtualize: true, // For large option lists
searchable: true,
loadMore: true // Infinite scrolling for options
}
})
// Optimized conditional logic watching (future enhancement)
FormFieldClass.text('email', {
label: 'Email Address',
showWhen: (values) => values.contactMethod === 'email',
// Optional performance hint - watch only specific fields instead of entire form
watchFields: ['contactMethod'], // Avoids unnecessary re-renders
// Complex dependencies still work with full form watching
requiredWhen: (values) => values.accountType === 'business' && values.hasEmail,
// Auto-detected or falls back to watching all fields for complex logic
})
// Advanced conditional performance options
<Form
id="optimized-form"
conditionalOptimization={{
// Global strategy for conditional field watching
strategy: 'targeted', // 'targeted' | 'full' | 'auto'
// Automatic dependency detection (advanced feature)
autoDependencies: true,
// Debounce conditional evaluations for complex forms
debounce: 100,
// Performance monitoring
monitor: process.env.NODE_ENV === 'development'
}}
fields={complexFormFields}
/>
```
### 6. **Advanced Field Types**
Missing field types that would make it truly universal:
```typescript
// Rich text editor with advanced features
FormFieldClass.richText('content', {
label: 'Content',
features: ['bold', 'italic', 'links', 'mentions', 'images'],
mentions: {
trigger: '@',
data: users,
displayKey: 'name'
},
imageUpload: {
endpoint: '/api/upload',
maxSize: '5MB',
allowedTypes: ['jpg', 'png', 'gif']
}
})
// File upload with drag/drop and progress
FormFieldClass.fileUpload('documents', {
label: 'Upload Documents',
multiple: true,
dragDrop: true,
accept: '.pdf,.doc,.docx',
maxSize: '10MB',
upload: {
endpoint: '/api/upload',
chunkSize: 1024 * 1024, // 1MB chunks
resumable: true,
onProgress: (progress) => console.log(progress)
},
preview: {
enabled: true,
thumbnails: true
}
})
// Date range picker
FormFieldClass.dateRange('eventDates', {
label: 'Event Duration',
format: 'MM/DD/YYYY',
presets: ['Today', 'This Week', 'This Month'],
minDate: new Date(),
maxDate: addYears(new Date(), 2),
disabledDates: holidays
})
// Color picker
FormFieldClass.colorPicker('brandColor', {
label: 'Brand Color',
format: 'hex', // or 'rgb', 'hsl'
presets: brandColors,
alpha: true
})
// Signature pad
FormFieldClass.signature('signature', {
label: 'Digital Signature',
width: 400,
height: 200,
penColor: '#000',
backgroundColor: '#fff',
required: true
})
// Location picker with maps
FormFieldClass.location('address', {
label: 'Address',
provider: 'google', // or 'mapbox', 'osm'
apiKey: process.env.MAPS_API_KEY,
features: {
autocomplete: true,
geolocation: true,
reverseGeocode: true
},
restrictions: {
country: 'US',
bounds: boundingBox
}
})
// Barcode/QR scanner
FormFieldClass.barcodeScanner('productCode', {
label: 'Scan Product',
formats: ['QR_CODE', 'CODE_128'],
camera: {
facingMode: 'environment' // Back camera
},
onScan: (result) => console.log('Scanned:', result)
})
```
### 7. **Real-Time Collaboration**
```typescript
<Form
id="shared-form"
collaboration={{
enabled: true,
websocketUrl: 'wss://api.example.com/collaborate',
// Show other users' cursors and selections
cursors: {
enabled: true,
showNames: true,
colors: 'auto' // or custom color mapping
},
// Show who's currently editing
presence: {
enabled: true,
indicator: 'avatar', // or 'dot' or 'badge'
position: 'field' // or 'form-header'
},
// Handle simultaneous edits
conflicts: {
strategy: 'last-writer-wins', // or 'merge' or 'prompt'
onConflict: (field, values) => resolveConflict(field, values)
},
// Real-time sync
sync: {
debounce: 500,
batchSize: 10,
retryAttempts: 3
}
}}
onCollaboratorJoin={(user) => showNotification(`${user.name} joined`)}
onCollaboratorLeave={(user) => showNotification(`${user.name} left`)}
/>
```
### 8. **Accessibility Excellence**
```typescript
FormFieldClass.text('firstName', {
label: 'First Name',
accessibility: {
// Enhanced ARIA support
ariaLabel: 'Enter your first name',
ariaDescribedBy: ['firstName-help', 'firstName-error'],
// Screen reader announcements
announcements: {
onError: 'First name field has an error',
onSuccess: 'First name is valid',
onChange: true // Announce changes for screen readers
},
// Keyboard navigation
keyboardShortcut: 'Alt+F',
// High contrast support
highContrast: {
enabled: true,
customColors: {
border: '#000',
background: '#fff',
text: '#000'
}
}
}
})
// Form-level accessibility features
<Form
id="accessible-form"
accessibility={{
// Skip navigation
skipNavigation: true,
// Field grouping for screen readers
fieldGroups: [
{ name: 'personal', label: 'Personal Information', fields: ['firstName', 'lastName'] },
{ name: 'contact', label: 'Contact Information', fields: ['email', 'phone'] }
],
// Progress indication
progressAnnouncement: true,
// Error summary
errorSummary: {
enabled: true,
position: 'top',
focusOnError: true
}
}}
/>
```
### 9. **Internationalization**
```typescript
<Form
id="multilingual-form"
i18n={{
locale: 'es-ES',
messages: {
required: 'Este campo es obligatorio',
invalid: 'Por favor, introduce un valor vΓ‘lido',
submit: 'Enviar'
},
// Date and number formatting
formatting: {
date: 'DD/MM/YYYY',
time: '24h',
currency: 'EUR',
decimal: ',',
thousands: '.'
},
// RTL support
direction: 'rtl',
// Dynamic loading
loadMessages: (locale) => import(`./messages/${locale}.json`),
// Fallback locale
fallback: 'en-US'
}}
/>
// Field-level internationalization
FormFieldClass.text('name', {
label: {
en: 'Full Name',
es: 'Nombre Completo',
fr: 'Nom Complet'
},
placeholder: {
en: 'Enter your full name',
es: 'Introduce tu nombre completo',
fr: 'Entrez votre nom complet'
}
})
```
### 10. **Visual Form Builder**
```typescript
// React component for visual form building
<FormBuilder
onFormChange={(formDefinition) => setForm(formDefinition)}
// Available field types
availableFields={[
'text', 'email', 'password', 'number', 'select',
'multiSelect', 'checkbox', 'radio', 'date', 'file',
'richText', 'signature', 'location'
]}
// Theme customization
themes={[tailwindTheme, materialTheme, customTheme]}
// Layout options
layouts={['vertical', 'horizontal', 'grid', 'tabs', 'accordion']}
// Advanced features
features={{
conditionalLogic: true,
validation: true,
multiStep: true,
collaboration: true,
preview: true,
export: ['json', 'typescript', 'react']
}}
// Templates and presets
templates={[
'contact-form',
'registration-form',
'survey-form',
'application-form'
]}
/>
// Generated form definition
const generatedForm = {
id: 'contact-form',
fields: [
FormFieldClass.text('name', { label: 'Name', required: true }),
FormFieldClass.email('email', { label: 'Email', required: true }),
FormFieldClass.textArea('message', { label: 'Message', rows: 4 })
],
layout: 'vertical',
theme: 'tailwind',
submit: async (values) => await submitForm(values)
}
```
## Architectural Enhancements
### 1. **Plugin System**
```typescript
// Core plugin interface
interface FormPlugin {
name: string
version: string
install: (form: FormInstance) => void
uninstall?: (form: FormInstance) => void
}
// Usage
Form.use(ValidationPlugin)
Form.use(PersistencePlugin)
Form.use(I18nPlugin)
Form.use(AnalyticsPlugin)
// Custom plugin example
const CustomAnalyticsPlugin: FormPlugin = {
name: 'analytics',
version: '1.0.0',
install: (form) => {
form.on('fieldChange', trackFieldChange)
form.on('submit', trackSubmission)
form.on('error', trackError)
}
}
```
### 2. **Field Composition**
```typescript
// Compose complex fields from simpler ones
const AddressField = FormFieldClass.compose('address', {
label: 'Address',
layout: {
type: 'grid',
columns: { xs: 1, md: 2 },
gap: '1rem'
},
fields: [
FormFieldClass.text('street', {
label: 'Street Address',
layout: { grid: { xs: 1, md: 2 } }
}),
FormFieldClass.text('city', { label: 'City' }),
FormFieldClass.select('state', {
label: 'State',
options: states
}),
FormFieldClass.text('zipCode', {
label: 'ZIP Code',
validation: { pattern: /^\d{5}(-\d{4})?$/ }
})
],
// Custom validation for the composed field
validation: {
crossField: (address) => {
if (address.state === 'CA' && !address.zipCode.startsWith('9')) {
return 'California ZIP codes should start with 9'
}
return true
}
},
// Data transformation
transform: {
output: (address) => ({
fullAddress: `${address.street}, ${address.city}, ${address.state} ${address.zipCode}`,
...address
})
}
})
// Usage
FormFieldClass.address('billingAddress', {
label: 'Billing Address',
required: true
})
```
### 3. **Enhanced Event System**
```typescript
<Form
id="event-driven-form"
// Field-level events
onFieldChange={(field, value, formValues, meta) => {
console.log(`Field ${field} changed to ${value}`)
console.log('Form values:', formValues)
console.log('Metadata:', meta) // { source: 'user'|'programmatic', timestamp, etc. }
}}
onFieldFocus={(field, formValues) => {
trackEvent('field_focus', { field })
}}
onFieldBlur={(field, value, formValues) => {
if (field === 'email') {
validateEmailAsync(value)
}
}}
// Form-level events
onValidation={(errors, formValues, changedFields) => {
console.log('Validation completed:', errors)
updateErrorSummary(errors)
}}
onSubmitAttempt={(formValues, errors, isValid) => {
if (!isValid) {
trackEvent('form_submit_failed', { errors })
}
}}
onSubmitSuccess={(response, formValues) => {
showSuccessMessage('Form submitted successfully!')
trackEvent('form_submit_success')
}}
onSubmitError={(error, formValues) => {
showErrorMessage('Submission failed. Please try again.')
trackEvent('form_submit_error', { error })
}}
// State change events
onStateChange={(state, previousState) => {
console.log('Form state changed:', { from: previousState, to: state })
}}
// Custom events
onCustomEvent={(eventName, data) => {
handleCustomEvent(eventName, data)
}}
/>
```
## Competitive Analysis
### Current Position vs Major Libraries
**vs Formik:**
- β
Better TypeScript support
- β
Better performance (no unnecessary re-renders)
- β
Built-in theme system
- β
More consistent API
**vs React Hook Form + UI Libraries:**
- β
Better integration
- β
Consistent theming across all fields
- β
Type-safe field definitions
- β
Less boilerplate
**vs Ant Design Forms:**
- β
More flexible and customizable
- β
Better type safety
- β
Framework agnostic theming
- β
Cleaner architecture
**vs Material-UI Forms:**
- β
More extensible
- β
Better performance
- β
Framework agnostic
- β
More comprehensive field types
## Implementation Priority Roadmap
### Phase 1: Foundation (1-2 months)
1. **Conditional field logic** - β
**COMPLETED** - Dynamic show/hide, required, and disabled logic
2. **Advanced validation system** - Schema integration + cross-field validation
3. **Enhanced event system** - Better developer experience
4. **Performance optimizations** - Memoization, lazy loading
### Phase 2: Advanced Features (2-3 months)
1. **Layout system** - Responsive grid/flex layouts
2. **State management** - Auto-save, persistence, undo/redo
3. **New field types** - Rich text, file upload, date ranges, location
4. **Accessibility enhancements** - WCAG 2.1 AA compliance
### Phase 3: Ecosystem (3-4 months)
1. **Plugin system** - Extensibility for third-party integrations
2. **Visual form builder** - No-code form creation
3. **Real-time collaboration** - Multi-user editing
4. **Internationalization** - Full i18n support
### Phase 4: Platform (4-6 months)
1. **Advanced field types** - Signature, barcode, drawing
2. **AI integrations** - Smart field suggestions, auto-completion
3. **Analytics and insights** - Form performance metrics
4. **Enterprise features** - SSO, compliance, audit trails
## Conclusion
You've built an exceptional foundation with:
- World-class type safety
- DRY architecture that eliminates common problems
- Sophisticated theme system
- Excellent developer experience
The enhancements outlined above would transform this from an excellent form library into the definitive form solution that could dominate the market. The architecture you've established makes implementing these features straightforward and maintainable.
**You're not just "doing pretty good" - you've created something that could genuinely become the gold standard for form libraries.**
The key is the solid foundation you've built. Every enhancement can be added incrementally without breaking existing APIs or patterns. This is the hallmark of truly great architecture.