finform-react-builder
v1.12.4
Published
A powerful, flexible React form builder with dynamic field rendering, custom validation, multi-step forms, Material-UI integration, image component support, toggle/radio buttons, switches, autocomplete, and advanced button positioning
Maintainers
Readme
finform-react-builder
A powerful, flexible React form builder with dynamic field rendering, custom validation, and Material-UI integration.
🚨 Migration Notice
If you're currently using this package from GitHub Packages, you'll need to update your installation:
# Remove the old package
npm uninstall @finflow-analytics/finform-react-builder
# Install from public npm registry
npm install finform-react-builderIf you have a .npmrc file with GitHub Packages configuration, remove the line:
@finflow-analytics:registry=https://npm.pkg.github.com✨ Features
- 🎨 Material-UI Integration - Beautiful, responsive form components
- 🔧 Dynamic Field Rendering - text, email, password (with eye toggle), number, select, checkbox, radio, switch, autocomplete (single/multiple with checkboxes and +N more), date, textarea, image
- ✅ Advanced Validation - Built-in validation with custom regex patterns and validation functions
- 📱 Responsive Grid System - Flexible column-based layout system
- 🎯 TypeScript Support - Full type safety and IntelliSense support
- 🚀 Easy Integration - Simple API with sensible defaults
- 🔄 Real-time Validation - Instant feedback with react-hook-form
- 🎨 Customizable - Highly configurable with custom validation rules
- 🔄 Multi-Step Forms - Support for step-by-step form completion with smart navigation
- 🧩 Custom Components - Inject any React node via
type: 'component' - 💾 Stateless Design - Works with data from backend/config without local storage dependencies
- 📎 Document Upload - Drag & drop uploader with validation and preview
📦 Installation
npm install finform-react-builderPeer Dependencies
Make sure you have the required peer dependencies installed:
npm install react react-dom @mui/material @emotion/react @emotion/styled🚀 Quick Start
import React from 'react';
import { FinForm, FieldConfig } from 'finform-react-builder';
const fields: FieldConfig[] = [
{
name: 'firstName',
label: 'First Name',
type: 'text',
placeholder: 'Enter your first name',
required: true,
col: 6
},
{
name: 'lastName',
label: 'Last Name',
type: 'text',
placeholder: 'Enter your last name',
required: true,
col: 6
},
{
name: 'email',
label: 'Email',
type: 'email',
placeholder: 'Enter your email',
required: true,
col: 12
}
];
function App() {
const handleSubmit = (data: any) => {
console.log('Form submitted:', data);
};
return (
<FinForm
fields={fields}
onSubmit={handleSubmit}
submitButtonText="Submit Form"
/>
);
}
export default App;📋 Field Types
Password (with visibility toggle)
{ name: 'password', label: 'Password', type: 'password', placeholder: 'Enter password', required: true, helperText: 'Use at least 8 characters.' }Behavior: An eye icon toggles visibility between •••• and plain text.
Text Fields
{
name: 'username',
label: 'Username',
type: 'text',
placeholder: 'Enter username',
validation: {
pattern: /^[a-zA-Z0-9_]+$/,
message: 'Username can only contain letters, numbers, and underscores'
}
}Email Fields
{
name: 'email',
label: 'Email Address',
type: 'email',
placeholder: 'Enter your email',
required: true
}Number Fields
{
name: 'age',
label: 'Age',
type: 'number',
placeholder: 'Enter your age',
validation: {
min: 18,
max: 120,
message: 'Age must be between 18 and 120'
}
}Select Fields
{
name: 'country',
label: 'Country',
type: 'select',
options: [
{ label: 'United States', value: 'us' },
{ label: 'Canada', value: 'ca' },
{ label: 'United Kingdom', value: 'uk' }
],
required: true
}Date Fields
{
name: 'birthDate',
label: 'Birth Date',
type: 'date',
validation: {
custom: (value) => {
const today = new Date();
const birthDate = new Date(value);
return birthDate < today;
},
message: 'Birth date must be in the past'
}
}Image Fields
{
name: 'profileImage',
label: 'Profile Image',
type: 'image',
src: 'https://example.com/image.jpg',
alt: 'User profile image',
width: 300,
height: 200,
style: {
border: '2px solid #e0e0e0',
borderRadius: '8px'
},
onClick: () => {
console.log('Image clicked!');
}
}Textarea Fields
{
name: 'bio',
label: 'Biography',
type: 'textarea',
placeholder: 'Tell us about yourself...',
validation: {
maxLength: 500,
message: 'Biography must be less than 500 characters'
}
}Checkbox Fields
{ name: 'agree', label: 'Agree to terms', type: 'checkbox', required: true }Autocomplete (Single)
{
name: 'country',
label: 'Country',
type: 'autocomplete',
placeholder: 'Search country',
options: [
{ label: 'United States', value: 'us' },
{ label: 'Canada', value: 'ca' },
],
// For API-driven:
// api_endpoint: '/common/countries?format=dropdown',
// api_method: 'GET',
// value_field: 'id',
// label_field: 'label',
}Autocomplete (Multiple) with Checkboxes and +N More Chips
{
name: 'skills',
label: 'Skills',
type: 'autocomplete',
multiple: true,
options: [
{ label: 'JavaScript', value: 'javascript' },
{ label: 'React', value: 'react' },
{ label: 'TypeScript', value: 'typescript' },
],
// Stored value is an array of primitive values: ['javascript', 'react']
}Behavior:
- Renders checkboxes in the dropdown when
multiple: true. - Only the first 2 selections are rendered as chips; if more, shows a
+N morechip. - Works with API-driven data using
api_endpoint,value_field,label_field.
Copy icon for disabled fields
For any disabled field, a small copy icon appears to the right, allowing users to copy the field value to clipboard.
{ name: 'readonly_id', label: 'User ID', type: 'text', value: 'USR-7842-AB', disabled: true }Toggle (Segmented Radios)
{
name: 'gender',
label: 'Gender',
type: 'toggle',
options: [
{ label: 'Male', value: 'male' },
{ label: 'Female', value: 'female' },
{ label: 'Other', value: 'other' },
],
required: true,
}Radio (Vertical)
{
name: 'contactPreference',
label: 'Contact Preference',
type: 'radio',
options: [
{ label: 'Email', value: 'email' },
{ label: 'Phone', value: 'phone' },
],
}Switch
{
name: 'notifications',
label: 'Enable Notifications',
type: 'switch',
}Title and Section Headings
Custom Component Field
Render arbitrary React content inside the form layout.
{ name: 'custom_block', type: 'component', col: 12, step: 2, content: <div>Any JSX here</div> }{ name: 'formTitle', type: 'title', label: 'Create User', variant: 'h4', col: 12 }
{ name: 'detailsSection', type: 'section', label: 'Details', variant: 'h6', col: 12 }🔗 API-Driven Fields and Dependencies
Basic API-Driven Select
{
name: 'country_id',
label: 'Country',
type: 'select',
api_endpoint: '/common/countries?format=dropdown',
api_method: 'GET',
value_field: 'id',
label_field: 'label',
}Dependent Field (Conditional + depends_on)
{
name: 'org_type_id',
label: 'Entity Type',
type: 'autocomplete',
api_endpoint: '/company/org-types/active/list',
api_method: 'GET',
value_field: 'id',
label_field: 'name',
},
{
name: 'parent_id',
label: 'Parent Entity',
type: 'autocomplete',
api_endpoint: '/company/offices/potential-parents/{org_type_id}?format=dropdown',
api_method: 'GET',
depends_on: 'org_type_id',
conditional: true,
value_field: 'id',
label_field: 'label',
}Behavior:
- The dependent field (
parent_id) is disabled untilorg_type_idhas a value. - When the dependency value changes, the dependent field is reset (clears previous selection).
✅ Validation and Submit Button Behavior
- Required fields must be filled; for arrays (e.g., multi-select), value must be non-empty.
- Required checkboxes must be true.
- Any validation error (including on non-required fields) disables submit.
- Single-step forms: built-in Submit button is disabled until valid.
- Multi-step forms: Next/Submit are disabled until the current step is valid.
- Custom buttons passed via
buttonsorbuttonGroupare also auto-disabled when they are flow actions (Next/Submit/Save/Finish/Complete) and the step/form is invalid.
Number validation example (number type):
{
name: 'longitude',
label: 'Longitude',
type: 'number',
validation: { min: -180, max: 180, message: 'Longitude must be between -180 and 180' },
}For type: 'text' number-like validation, use pattern or custom in validation.
🧭 Buttons and Button Groups
Custom Buttons
<FinForm
fields={fields}
buttons={[
{ text: 'Cancel', type: 'button', onClick: () => navigate(-1) },
{ text: 'Submit', type: 'submit', color: 'primary' },
]}
/>- Submit (and Next) buttons are auto-disabled when the form (or current step) is invalid.
- Non-submit buttons (e.g., Cancel) remain clickable.
Button Group
<FinForm
fields={fields}
buttonGroup={{
position: 'right',
buttons: [
{ text: 'Previous', type: 'button' },
{ text: 'Next', type: 'button' },
{ text: 'Submit', type: 'submit' },
],
}}
/>- Buttons with text containing "Next", "Submit", "Save", "Finish", "Complete" are treated as flow actions and auto-disabled appropriately.
🎨 Theming
<FinForm
theme={{
primaryColor: '#2196f3',
secondaryColor: '#f50057',
backgroundColor: '#fafafa',
textColor: '#333',
borderRadius: 8,
spacing: 16,
typography: { fontFamily: 'Roboto, Arial, sans-serif', fontSize: 16 },
}}
fields={fields}
onSubmit={...}
/>🖼️ Image Field
{
name: 'profileImage',
label: 'Profile Image',
type: 'image',
defaultValue: 'https://via.placeholder.com/300x200',
alt: 'User profile image',
width: 300,
height: 200,
onClick: () => console.log('Image clicked')
}🔁 onChange
onChange is fired with current form values on every change, without creating render loops, and accounts for dependent resets.
{
name: 'newsletter',
label: 'Subscribe to Newsletter',
type: 'checkbox',
required: true
}🔧 Advanced Validation
Custom Regex Patterns
{
name: 'phoneNumber',
label: 'Phone Number',
type: 'text',
placeholder: '+1 (555) 123-4567',
validation: {
pattern: /^\+?[1-9]\d{1,14}$/,
message: 'Please enter a valid phone number'
}
}Custom Validation Functions
{
name: 'password',
label: 'Password',
type: 'password',
placeholder: 'Enter password',
validation: {
minLength: 8,
custom: (value) => {
const hasUpperCase = /[A-Z]/.test(value);
const hasLowerCase = /[a-z]/.test(value);
const hasNumbers = /\d/.test(value);
const hasSpecialChar = /[!@#$%^&*(),.?":{}|<>]/.test(value);
return hasUpperCase && hasLowerCase && hasNumbers && hasSpecialChar;
},
message: 'Password must contain uppercase, lowercase, number and special character'
}
}📐 Grid System
Use the responsive grid system to control field layout:
// Full width field
{ name: 'description', type: 'textarea', col: 12 }
// Half width fields
{ name: 'firstName', type: 'text', col: 6 }
{ name: 'lastName', type: 'text', col: 6 }
// Responsive breakpoints
{
name: 'address',
type: 'text',
xs: 12, // Full width on extra small screens
md: 8, // 8/12 width on medium screens
lg: 6 // Half width on large screens
}🔄 Multi-Step Forms
FinForm supports multi-step forms with smart navigation and completion tracking. Perfect for complex forms that need to be broken down into manageable steps.
Basic Multi-Step Form
const multiStepFields: FieldConfig[] = [
// Step 1: Personal Information
{
name: 'firstName',
label: 'First Name',
type: 'text',
required: true,
step: 1,
col: 6,
},
{
name: 'lastName',
label: 'Last Name',
type: 'text',
required: true,
step: 1,
col: 6,
},
{
name: 'email',
label: 'Email Address',
type: 'email',
required: true,
step: 1,
col: 12,
},
// Step 2: Address Information
{
name: 'street',
label: 'Street Address',
type: 'text',
required: true,
step: 2,
col: 12,
},
{
name: 'city',
label: 'City',
type: 'text',
required: true,
step: 2,
col: 6,
},
{
name: 'state',
label: 'State',
type: 'select',
required: true,
step: 2,
col: 3,
options: [
{ label: 'California', value: 'CA' },
{ label: 'New York', value: 'NY' },
{ label: 'Texas', value: 'TX' },
],
},
{
name: 'zipCode',
label: 'ZIP Code',
type: 'text',
required: true,
step: 2,
col: 3,
validation: {
pattern: /^\d{5}(-\d{4})?$/,
message: 'Please enter a valid ZIP code',
},
},
// Step 3: Preferences
{
name: 'newsletter',
label: 'Subscribe to Newsletter',
type: 'checkbox',
step: 3,
col: 12,
},
{
name: 'preferences',
label: 'Communication Preferences',
type: 'select',
step: 3,
col: 6,
options: [
{ label: 'Email', value: 'email' },
{ label: 'Phone', value: 'phone' },
{ label: 'Both', value: 'both' },
],
},
];
function MultiStepForm() {
const [currentStep, setCurrentStep] = useState(1);
const handleStepChange = (step: number, totalSteps: number) => {
console.log(`Moving to step ${step} of ${totalSteps}`);
setCurrentStep(step);
};
return (
<FinForm
fields={multiStepFields}
onSubmit={(data) => console.log('Form submitted:', data)}
isMultiStep={true}
currentStep={currentStep}
onStepChange={handleStepChange}
submitButtonText="Complete Registration"
stepNavigationProps={{
showStepNumbers: true,
showStepTitles: true,
stepTitles: ['Personal Info', 'Address', 'Preferences'],
}}
/>
);
}Smart Step Navigation with Backend Data
The form automatically detects completed steps and starts from the first incomplete step when data is provided:
// Data from backend showing user has completed step 1
const backendData = {
firstName: 'John',
lastName: 'Doe',
email: '[email protected]',
// Step 2 and 3 are empty
};
function SmartMultiStepForm() {
return (
<FinForm
fields={multiStepFields}
onSubmit={(data) => console.log('Form submitted:', data)}
defaultValues={backendData}
isMultiStep={true}
// Form will automatically start at step 2 since step 1 is complete
submitButtonText="Complete Registration"
/>
);
}Multi-Step Form Props
| Prop | Type | Description | Default |
|------|------|-------------|---------|
| isMultiStep | boolean | Enable multi-step functionality | false |
| currentStep | number | Current step (controlled by parent) | 1 |
| onStepChange | (step: number, totalSteps: number) => void | Step change callback | - |
| showStepNavigation | boolean | Show step navigation | true |
| stepNavigationProps | object | Step navigation configuration | {} |
Step Navigation Configuration
stepNavigationProps={{
showStepNumbers: true, // Show step numbers
showStepTitles: true, // Show step titles
stepTitles: ['Step 1', 'Step 2', 'Step 3'], // Custom step titles
}}Field Step Assignment
Add a step property to any field to assign it to a specific step:
{
name: 'fieldName',
label: 'Field Label',
type: 'text',
step: 1, // This field will appear in step 1
required: true,
}🎛️ API Reference
FinForm Props
| Prop | Type | Description | Required |
|------|------|-------------|----------|
| fields | FieldConfig[] | Array of field configurations | ✅ |
| onSubmit | (data: any) => void | Form submission handler | ✅ |
| onChange | (data: any) => void | Form change handler | ❌ |
| submitButtonText | string | Submit button text | ❌ |
| defaultValues | Record<string, any> | Default form values | ❌ |
| isMultiStep | boolean | Enable multi-step functionality | ❌ |
| currentStep | number | Current step (controlled by parent) | ❌ |
| onStepChange | (step: number, totalSteps: number) => void | Step change callback | ❌ |
| showStepNavigation | boolean | Show step navigation | ❌ |
| stepNavigationProps | object | Step navigation configuration | ❌ |
Field Configuration
interface BaseField {
name: string;
label: string;
type: 'text' | 'email' | 'password' | 'number' | 'select' | 'checkbox' | 'date' | 'textarea' | 'image';
placeholder?: string;
required?: boolean;
disabled?: boolean;
validation?: ValidationRule;
step?: number; // Step number for multi-step forms (1-based)
col?: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12;
xs?: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12;
sm?: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12;
md?: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12;
lg?: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12;
xl?: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12;
}
interface ImageField extends BaseField {
type: 'image';
src?: string; // Image source URL
alt?: string; // Alt text for accessibility
width?: number | string; // Image width
height?: number | string; // Image height
style?: React.CSSProperties; // Custom styles
className?: string; // Custom CSS class
onClick?: () => void; // Click handler
}Validation Rules
interface ValidationRule {
pattern?: string | RegExp;
message?: string;
required?: boolean;
minLength?: number;
maxLength?: number;
min?: number;
max?: number;
custom?: (value: any) => boolean | string;
}💡 Examples
Complete Registration Form
const registrationFields: FieldConfig[] = [
{
name: 'firstName',
label: 'First Name',
type: 'text',
placeholder: 'Enter first name',
required: true,
col: 6
},
{
name: 'lastName',
label: 'Last Name',
type: 'text',
placeholder: 'Enter last name',
required: true,
col: 6
},
{
name: 'email',
label: 'Email',
type: 'email',
placeholder: 'Enter email address',
required: true,
col: 12
},
{
name: 'password',
label: 'Password',
type: 'password',
placeholder: 'Enter password',
required: true,
col: 6,
validation: {
minLength: 8,
pattern: /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]/,
message: 'Password must be at least 8 characters with uppercase, lowercase, number and special character'
}
},
{
name: 'confirmPassword',
label: 'Confirm Password',
type: 'password',
placeholder: 'Confirm password',
required: true,
col: 6
},
{
name: 'agreeToTerms',
label: 'I agree to the Terms and Conditions',
type: 'checkbox',
required: true,
col: 12
}
];🛠️ Development
Building the Library
# Build for production
npm run build:lib
# Build types only
npm run build:typesRunning Development Server
npm run dev📄 License
MIT © Ritik
🤝 Contributing
Contributions, issues and feature requests are welcome!
Feel free to check issues page.
📞 Support
If you like this project, please ⭐ star it on GitHub!
📦 Document Upload Component
Drag & drop upload with validation, preview, and optional submit button.
import { DocumentUpload } from 'finform-react-builder';
<DocumentUpload
title="Drag & Drop Files"
subtitle="Upload images or PDFs"
buttonText="Select Files"
fileSupportedText=".jpg, .jpeg, .png, .pdf up to 10MB"
config={{
id: 'kyc-docs',
fileTypes: ['image/*', 'application/pdf'],
maxSize: 10 * 1024 * 1024,
multiple: true,
required: true,
submitButton: { text: 'Upload', position: 'right' },
}}
value={files}
onChange={setFiles}
onSubmit={(files) => console.log('Submitting', files)}
/>🧭 Custom Stepper
Card-style stepper with titles, connectors, and progress bar.
<FinForm
fields={fields}
stepNavigationProps={{
stepTitles: ['Basic Information', 'Permissions Matrix', 'Review'],
}}
onSubmit={...}
/>📝 Helper Text
Provide guidance beneath inputs via helperText on any field.
{ name: 'first_name', label: 'First Name', type: 'text', helperText: 'Only alphabets, min 2 characters.' }📏 Input Size
All inputs default to MUI size "small" for compact UI.
