Plugin System
The Formitiva plugin system enables modular extension of the framework through a centralized registry. Plugins can register field components, validation handlers, submission handlers, and lifecycle hooks while ensuring controlled conflict resolution and safe unregistration.
Overview
Plugins are installed using a single entry point:
registerPlugin(plugin, options)
A plugin may contribute:
- Field components
- Per-field custom validators
- Type-level validators
- Form-level validators
- Submission handlers
- Lifecycle hooks (
setup,cleanup)
The registry:
- Detects and resolves registration conflicts
- Tracks ownership of registered items
- Supports controlled overrides
- Allows safe unregistration
Plugin Interface
A plugin implements the FormitivaPlugin interface:
interface FormitivaPlugin {
name: string;
version: string;
description?: string;
components?: Record<string, React.ComponentType>;
fieldCustomValidators?: Record<
string,
Record<string, FieldCustomValidationHandler>
>;
fieldTypeValidators?: Record<
string,
FieldTypeValidationHandler
>;
formValidators?: Record<
string,
FormValidationHandler
>;
submissionHandlers?: Record<
string,
FormSubmissionHandler
>;
setup?: () => void;
cleanup?: () => void;
}
Plugin Properties
name
Unique plugin identifier. Must not collide with other registered plugins.
version
Plugin version string.
components
Registers field components by field type.
components: {
point2d: Point2DInput
}
fieldCustomValidators
Registers per-field custom validators grouped by category.
- Category is typically the form definition name.
- Structure:
category → handlerName → handler
fieldCustomValidators: {
point2d: {
nonNegativePoint: handler
}
}
fieldTypeValidators
Registers type-level validators by field type.
- Executed before per-field custom validation.
- Applied globally to all fields of that type.
fieldTypeValidators: {
point2d: typeValidator
}
formValidators
Registers named form-level validation handlers.
formValidators: {
'point2d:regionValidator': regionValidator
}
submissionHandlers
Registers named submission handlers.
submissionHandlers: {
'point2d:alertSubmission': handler
}
setup
Optional hook invoked after successful registration.
cleanup
Optional hook invoked during unregistration.
Example Plugin
export const PointPlugin: FormitivaPlugin = {
name: 'point_plugin',
version: '0.1.0',
components: {
point2d: Point2DInput,
},
fieldCustomValidators: {
point2d: {
nonNegativePoint,
},
},
formValidators: {
'point2d:regionValidator': regionValidator,
},
submissionHandlers: {
'point2d:alertSubmission': alertSubmissionHandler,
},
setup: () => {
console.log('PointPlugin registered');
},
cleanup: () => {
console.log('PointPlugin unregistered');
},
};
Registering a Plugin
import { registerPlugin } from '@formitiva/react';
import { PointPlugin } from './PointPlugin';
registerPlugin(PointPlugin, {
conflictResolution: 'warn',
});
Registration Flow
When registerPlugin is called, the registry:
- Detects conflicts with existing registrations.
- Resolves conflicts using the configured strategy.
- Registers items in their respective registries.
- Calls
plugin.setup()(if provided). - Records ownership metadata.
Conflict Resolution
You can control conflict behavior using:
registerPlugin(plugin, {
conflictResolution: 'error' | 'warn' | 'override' | 'skip'
});
Strategies
| Strategy | Behavior |
|---|---|
error | Throws immediately (default) |
warn | Logs warning and skips conflicting items |
override | Replaces existing registration |
skip | Silently skips conflicting items |
You may also provide a custom resolver:
registerPlugin(plugin, {
onConflict(conflict) {
return 'override';
}
});
Validator Categories and Lookup
Per-Field Custom Validators
Custom validators are grouped by category (usually the form definition name).
When a field specifies:
validationHandlerName: "nonNegativePoint"
The system resolves the handler using:
- The current form definition name as category
- The provided handler name
To reference a specific category explicitly:
validationHandlerName: ["point2d", "nonNegativePoint"]
Type-Level Validators
Type-level validators:
- Are registered by field
type - Execute before per-field custom validation
- Apply globally to all fields of that type
Unregistering a Plugin
unregisterPlugin(pluginName, removeRegistrations);
If removeRegistrations is true:
- All components, validators, and handlers owned by the plugin are removed.
- The
cleanup()hook is invoked.
Ownership tracking ensures only items registered by the plugin are removed.
Ownership Model
The registry maintains internal ownership mappings for:
- Components
- Field validators
- Form validators
- Submission handlers
This enables:
- Safe unregistration
- Controlled overrides
- Deterministic conflict handling
Best Practices
- Keep plugins modular and feature-focused.
- Namespace validator and handler names (e.g.,
point2d:regionValidator). - Register per-field validators under the relevant form definition.
- Use
setupandcleanupfor runtime initialization and teardown. - Prefer
conflictResolution: 'warn'during development.