-
Notifications
You must be signed in to change notification settings - Fork 0
Home
A Lightweight, Extensible Validation Core for React Applications
React Validator Form is a lightweight, extensible validation framework designed for React applications that require full control over validation logic without the constraints of opinionated form libraries.
Unlike full-featured solutions such as Formik or React Hook Form, this project focuses on validation as a first-class concern, allowing teams to:
- Design custom validation rules
- Maintain a clear separation between UI and validation logic
- Extend validation behavior incrementally as application complexity grows
- Learn and build their own form validation architecture
This project can be used as:
- A foundation for custom form systems
- A reference implementation for rule-based validation
- A starter template for enterprise or internal tools
The architecture is guided by the following principles:
2.1 Separation of Concerns
- Validation logic is isolated from UI components
- Rules are reusable, composable, and testable
2.2 Extensibility
- Validation rules follow an object-oriented design
- New rules can be added without modifying existing logic
2.3 Predictability
- Each field produces a deterministic validation result
- Validation output structure is consistent across rules
2.4 Minimalism
- No external validation dependencies
- No forced schema or hook usage
- Easy to reason about and debug
4.1 BaseRule (Validation Contract) All validation rules inherit from a shared abstract base:
`export abstract class BaseRule { abstract test(params: RuleParam): boolean; abstract res(params: RuleParam): RuleResponse;
message(orgMsg: string, fieldName: string, arg: string): string { return orgMsg .replace("{#fieldName}", fieldName) .replace("{#arg}", arg); } }`
Responsibilities:
test() → Determine if the rule passes res() → Return a standardized validation response message() → Normalize error message formatting
This enforces consistency across all validation rules.
4.2 Rule Keys & Mapping
Rules are referenced by semantic keys:
export enum RuleKey { required = "required", phone = "phone", }
Mapping keys to rule instances enables dynamic rule execution:
const RulesMapping = [ { ruleName: RuleKey.required, inst: new RequiredRule() }, { ruleName: RuleKey.phone, inst: new PhoneRule() }, ];
Benefits:
- Centralized rule registration
- Easy rule replacement or extension
- Clear contract between UI and validation engine
4.3 Validator Engine
The Validator acts as the execution engine:
Validator.run( rules: Rules, fieldName: string, value: string ): FieldResult
Returns:
{ isValid: boolean; res?: RuleResponse[]; }
- Executes all configured rules for a field
- Aggregates rule failures
- Produces a single validation result per field
4.4 Field Components
Field components (e.g. Textbox) are responsible for:
- Tracking field value
- Triggering validation on events (change, blur, submit)
- Rendering validation feedback
Typical flow:
const result = validator.run(rules, name, value); setErrors(result.res ?? []);
UI components remain stateless with respect to validation logic, improving reusability.
5.1 Installation
git clone https://github.com/duyetvv/react-validator-form.git
cd react-validator-form
npm install
5.2 Development
npm start
# or
npm run dev
5.3 Build
npm run build
<Textbox name="phone" label="Phone Number" rules={{ required: true, phone: true }} store={store} />
Validation lifecycle:
- User updates input
- Field triggers validator
- Rules are executed
- Errors are rendered (if any)
- Form validity is recalculated
7.1 Creating a Custom Rule
`class MinLengthRule extends BaseRule { test({ val, arg }) { return val.length >= Number(arg); }
res({ name, arg }) {
return {
name,
message: Minimum length is ${arg},
};
}
}`
7.2 Register the Rule
RulesMapping.push({ ruleName: RuleKey.minLength, inst: new MinLengthRule(), });
This project intentionally avoids enforcing a state library.
You may integrate:
React Context Redux / Zustand Custom store pattern (as shown)
Recommendation: Keep validation state centralized at form level, while field components remain isolated.