@enerjisaformlibrary/formbuilder-react
v1.0.22
Published
Professional drag-and-drop form builder React component with 20+ field types, multi-step wizard, conditional logic, undo/redo, and JSON export
Maintainers
Readme
@enerjisaformlibrary/formbuilder-react
Professional drag-and-drop form builder React component with 20+ field types, multi-step wizard support, conditional logic, undo/redo history, and JSON export/import.
Features
- 20+ Field Types: Text, textarea, number, email, phone, URL, password, date, time, date range, dropdown, checkbox, radio, toggle, multi-select, file upload, signature pad, star rating, slider, color picker, rich text editor, autocomplete, QR code, pattern format, and more
- Drag & Drop: Intuitive drag-and-drop interface for building forms
- Multi-Step Wizard: Create multi-step forms with progress indicator
- Conditional Logic: Show/hide/enable/disable fields based on conditions
- Undo/Redo: Full undo/redo support with 50-state history
- Grid Layout: 12-column responsive grid system
- Containers: Nested row/column layouts within containers
- JSON Export/Import: Export forms as JSON and import them back
- Custom Styling: Per-field CSS class customization
- Form Versioning: Save and restore form versions
- Tooltips: Help text support for fields
- Actions System: onClick, onChange, onFocus, onBlur event handlers
- Tab Index: Keyboard navigation order for form fields
Installation
npm install @enerjisaformlibrary/formbuilder-reactQuick Start
import { FormBuilder } from '@enerjisaformlibrary/formbuilder-react';
import '@enerjisaformlibrary/formbuilder-react/styles.css';
function App() {
const handleSave = (formSchema) => {
console.log('Form saved:', formSchema);
// Save to your database
};
const handleChange = (formSchema) => {
console.log('Form changed:', formSchema);
};
return (
<FormBuilder
onSave={handleSave}
onChange={handleChange}
/>
);
}Props
| Prop | Type | Required | Description |
|------|------|----------|-------------|
| initialSchema | FormSchema | No | Initial form schema to load |
| onSave | (schema: FormSchema) => void | No | Called when user clicks Save button |
| onChange | (schema: FormSchema) => void | No | Called on every form change |
Form Schema Structure
interface FormSchema {
id: string;
name: string;
description?: string;
isMultiStep: boolean;
currentVersion: number;
rows: FormRow[]; // For single-page forms
steps?: FormStep[]; // For multi-step forms
settings?: FormSettings;
submissionConfig?: SubmissionConfig;
versions?: FormVersion[];
}
interface FormRow {
id: string;
columns: FormColumn[];
conditionalLogic?: ConditionalLogic;
}
interface FormColumn {
id: string;
width: number; // 1-12 (grid columns)
fields: FormField[];
responsiveWidth?: ResponsiveWidth;
}
interface FormField {
id: string;
type: FieldType;
props: FieldProps;
validation?: FieldValidation;
conditionalLogic?: ConditionalLogic;
customStyle?: CustomStyle;
}Field Types
Basic Fields
input- Single line text inputtextarea- Multi-line text areanumber- Numeric inputemail- Email input with validationpassword- Password inputphone- Phone number inputurl- URL input
Date & Time
date- Date pickertime- Time pickerdaterange- Date range picker
Selection Fields
dropdown- Single select dropdowncheckbox- Checkbox fieldradio- Radio button grouptoggle- Toggle switchmultiselect- Multi-select with tags
Advanced Fields
file- File upload with drag-dropsignature- Signature pad with canvas drawingrating- Star rating (configurable max stars)slider- Range slider with min/maxcolor- Color picker with hex inputrichtext- Rich text editorautocomplete- Autocomplete inputpattern- Pattern format input (phone, credit card, etc.)qrcode- Static QR code display
Static Elements
header- Section headerlabel- Static label textparagraph- Paragraph textdivider- Horizontal dividerspacer- Vertical spaceralert- Alert/notification boximage- Image placeholderbutton- Submit/reset/custom action buttons
Structure
container- Grouping element with nested rows/columns
Field Props
interface FieldProps {
key: string; // Unique field key for form data
label?: string; // Field label
placeholder?: string; // Placeholder text
tooltip?: string; // Help tooltip text
optionsString?: string; // Options for dropdown/radio/checkbox (one per line)
size?: 'small' | 'medium' | 'large';
autoFocus?: boolean;
tabIndex?: number; // Keyboard navigation order (positive = order, -1 = skip, 0 = natural order)
htmlAttributes?: Record<string, string>;
// Button specific
buttonConfig?: {
buttonType: 'submit' | 'reset' | 'button';
variant: 'primary' | 'secondary' | 'outline' | 'ghost' | 'destructive';
actionType?: 'submit' | 'reset' | 'navigate' | 'custom';
navigateUrl?: string;
customAction?: string;
};
// Pattern format specific
patternConfig?: {
format: 'phone' | 'creditCard' | 'date' | 'ssn' | 'custom';
mask?: string;
customPattern?: string;
};
// QR Code specific
qrCodeConfig?: {
value: string;
size: number;
};
// Container specific
containerConfig?: {
rows: ContainerRow[];
gap?: number;
padding?: number;
borderStyle?: 'none' | 'solid' | 'dashed';
};
// Rating specific
maxRating?: number;
// Slider specific
min?: number;
max?: number;
step?: number;
}Tab Index Usage
The tabIndex property controls keyboard navigation order when users press Tab to move between form fields:
| Value | Behavior | |-------|----------| | Positive (1, 2, 3...) | Field is focused in order from lowest to highest tabIndex | | 0 | Field follows natural DOM order | | -1 | Field is skipped during Tab navigation |
Example: To create a custom tab order:
- First Name: tabIndex = 1
- Email: tabIndex = 2
- Last Name: tabIndex = 3
- Phone: tabIndex = 4
When user presses Tab, they'll move: First Name → Email → Last Name → Phone
Important: tabIndex only works in Preview Mode. In editor mode, fields don't receive focus in the same way.
Validation
interface FieldValidation {
required?: boolean;
minLength?: number;
maxLength?: number;
min?: number;
max?: number;
pattern?: string;
customMessage?: string;
}Conditional Logic
interface ConditionalLogic {
enabled: boolean;
action: 'show' | 'hide' | 'enable' | 'disable' | 'require';
conditions: Condition[];
logicOperator: 'and' | 'or';
}
interface Condition {
fieldKey: string;
operator: 'equals' | 'notEquals' | 'contains' | 'notContains' |
'greaterThan' | 'lessThan' | 'isEmpty' | 'isNotEmpty';
value: string;
}Custom Styling
interface CustomStyle {
containerClassName?: string; // CSS class for field container
labelClassName?: string; // CSS class for label
inputClassName?: string; // CSS class for input element
css?: string; // Custom CSS (applied inline)
}Actions System
Each field can have event handlers:
interface FieldActions {
onClick?: FieldAction;
onChange?: FieldAction;
onFocus?: FieldAction;
onBlur?: FieldAction;
}
interface FieldAction {
type: 'showMessage' | 'hideField' | 'showField' | 'clearField' |
'setFieldValue' | 'focusField' | 'submitForm' | 'custom' | 'code';
args?: {
message?: string;
targetFieldKey?: string;
value?: string;
code?: string;
};
}Multi-Step Forms
interface FormStep {
id: string;
title: string;
description?: string;
order: number;
rows: FormRow[];
validation?: {
validateOnNext?: boolean;
allowSkip?: boolean;
};
}Example: Complete Form Schema
{
"id": "contact-form",
"name": "Contact Form",
"isMultiStep": false,
"currentVersion": 1,
"rows": [
{
"id": "row-1",
"columns": [
{
"id": "col-1",
"width": 6,
"fields": [
{
"id": "field-1",
"type": "input",
"props": {
"key": "firstName",
"label": "First Name",
"placeholder": "Enter your first name",
"tabIndex": 1
},
"validation": {
"required": true,
"minLength": 2
}
}
]
},
{
"id": "col-2",
"width": 6,
"fields": [
{
"id": "field-2",
"type": "input",
"props": {
"key": "lastName",
"label": "Last Name",
"placeholder": "Enter your last name",
"tabIndex": 2
},
"validation": {
"required": true
}
}
]
}
]
},
{
"id": "row-2",
"columns": [
{
"id": "col-3",
"width": 12,
"fields": [
{
"id": "field-3",
"type": "email",
"props": {
"key": "email",
"label": "Email Address",
"placeholder": "[email protected]",
"tooltip": "We'll never share your email",
"tabIndex": 3
},
"validation": {
"required": true
}
}
]
}
]
},
{
"id": "row-3",
"columns": [
{
"id": "col-4",
"width": 12,
"fields": [
{
"id": "field-4",
"type": "dropdown",
"props": {
"key": "country",
"label": "Country",
"optionsString": "USA\nCanada\nUK\nGermany\nFrance\nOther",
"tabIndex": 4
}
}
]
}
]
},
{
"id": "row-4",
"columns": [
{
"id": "col-5",
"width": 12,
"fields": [
{
"id": "field-5",
"type": "textarea",
"props": {
"key": "message",
"label": "Message",
"placeholder": "How can we help you?",
"tabIndex": 5
},
"validation": {
"required": true,
"minLength": 10,
"maxLength": 500
}
}
]
}
]
},
{
"id": "row-5",
"columns": [
{
"id": "col-6",
"width": 12,
"fields": [
{
"id": "field-6",
"type": "button",
"props": {
"key": "submit",
"label": "Submit",
"buttonConfig": {
"buttonType": "submit",
"variant": "primary",
"actionType": "submit"
}
}
}
]
}
]
}
]
}Integration with JetDesk
import { FormBuilder } from '@enerjisaformlibrary/formbuilder-react';
import '@enerjisaformlibrary/formbuilder-react/styles.css';
function FormEditor({ formId }) {
const [initialSchema, setInitialSchema] = useState(null);
useEffect(() => {
// Load existing form from database
fetch(`/api/forms/${formId}`)
.then(res => res.json())
.then(data => setInitialSchema(data.schema));
}, [formId]);
const handleSave = async (schema) => {
await fetch(`/api/forms/${formId}`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ schema })
});
};
if (!initialSchema) return <div>Loading...</div>;
return (
<FormBuilder
initialSchema={initialSchema}
onSave={handleSave}
/>
);
}Keyboard Shortcuts
- Ctrl/Cmd + Z: Undo
- Ctrl/Cmd + Shift + Z: Redo
Browser Support
- Chrome (latest)
- Firefox (latest)
- Safari (latest)
- Edge (latest)
Changelog
1.0.22
- Fixed: tabIndex now properly applied to all interactive field types (dropdown, checkbox, radio, toggle, slider, autocomplete, daterange)
1.0.21
- Added: Comprehensive undo/redo functionality with 50-state history
- Each form modification creates a separate history entry
License
MIT
