finform-react-builder
Version:
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
621 lines (539 loc) • 14.1 kB
Markdown
A powerful, flexible React form builder with dynamic field rendering, custom validation, and Material-UI integration.
[](https://www.npmjs.com/package/finform-react-builder)
[](https://opensource.org/licenses/MIT)
**If you're currently using this package from GitHub Packages**, you'll need to update your installation:
```bash
npm uninstall @finflow-analytics/finform-react-builder
npm install finform-react-builder
```
If you have a `.npmrc` file with GitHub Packages configuration, remove the line:
```
@finflow-analytics:registry=https://npm.pkg.github.com
```
- 🎨 **Material-UI Integration** - Beautiful, responsive form components
- 🔧 **Dynamic Field Rendering** - Support for text, email, password, number, select, checkbox, date, and textarea fields
- ✅ **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
- 💾 **Stateless Design** - Works with data from backend/config without local storage dependencies
```bash
npm install finform-react-builder
```
Make sure you have the required peer dependencies installed:
```bash
npm install react react-dom @mui/material @emotion/react @emotion/styled
```
```tsx
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;
```
```tsx
{
name: 'username',
label: 'Username',
type: 'text',
placeholder: 'Enter username',
validation: {
pattern: /^[a-zA-Z0-9_]+$/,
message: 'Username can only contain letters, numbers, and underscores'
}
}
```
```tsx
{
name: 'email',
label: 'Email Address',
type: 'email',
placeholder: 'Enter your email',
required: true
}
```
```tsx
{
name: 'age',
label: 'Age',
type: 'number',
placeholder: 'Enter your age',
validation: {
min: 18,
max: 120,
message: 'Age must be between 18 and 120'
}
}
```
```tsx
{
name: 'country',
label: 'Country',
type: 'select',
options: [
{ label: 'United States', value: 'us' },
{ label: 'Canada', value: 'ca' },
{ label: 'United Kingdom', value: 'uk' }
],
required: true
}
```
```tsx
{
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'
}
}
```
```tsx
{
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!');
}
}
```
```tsx
{
name: 'bio',
label: 'Biography',
type: 'textarea',
placeholder: 'Tell us about yourself...',
validation: {
maxLength: 500,
message: 'Biography must be less than 500 characters'
}
}
```
```tsx
{
name: 'newsletter',
label: 'Subscribe to Newsletter',
type: 'checkbox',
required: true
}
```
```tsx
{
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'
}
}
```
```tsx
{
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 = /[!@
return hasUpperCase && hasLowerCase && hasNumbers && hasSpecialChar;
},
message: 'Password must contain uppercase, lowercase, number and special character'
}
}
```
Use the responsive grid system to control field layout:
```tsx
// 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
}
```
FinForm supports multi-step forms with smart navigation and completion tracking. Perfect for complex forms that need to be broken down into manageable steps.
```tsx
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'],
}}
/>
);
}
```
The form automatically detects completed steps and starts from the first incomplete step when data is provided:
```tsx
// Data from backend showing user has completed step 1
const backendData = {
firstName: 'John',
lastName: 'Doe',
email: 'john.doe@example.com',
// 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"
/>
);
}
```
| 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 | `{}` |
```tsx
stepNavigationProps={{
showStepNumbers: true, // Show step numbers
showStepTitles: true, // Show step titles
stepTitles: ['Step 1', 'Step 2', 'Step 3'], // Custom step titles
}}
```
Add a `step` property to any field to assign it to a specific step:
```tsx
{
name: 'fieldName',
label: 'Field Label',
type: 'text',
step: 1, // This field will appear in step 1
required: true,
}
```
| 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 | ❌ |
```tsx
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
}
```
```tsx
interface ValidationRule {
pattern?: string | RegExp;
message?: string;
required?: boolean;
minLength?: number;
maxLength?: number;
min?: number;
max?: number;
custom?: (value: any) => boolean | string;
}
```
```tsx
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
}
];
```
```bash
npm run build:lib
npm run build:types
```
```bash
npm run dev
```
MIT © [Ritik](https://github.com/finflow-analytics/finform)
Contributions, issues and feature requests are welcome!
Feel free to check [issues page](https://github.com/finflow-analytics/finform/issues).
If you like this project, please ⭐ star it on [GitHub](https://github.com/finflow-analytics/finform)!