@enerjisaformlibrary/formbuilder-react
v1.0.9
Published
Standalone drag-and-drop form builder React component with CSS included. 20+ field types, conditional logic, multi-step forms.
Maintainers
Readme
@enerjisaformlibrary/formbuilder-react
A comprehensive, standalone drag-and-drop form builder React component. All UI components, styles, and dependencies are bundled - only React is required as a peer dependency.
Table of Contents
- Installation
- Quick Start
- FormBuilder Props
- Exported Components
- Form Schema Structure
- Field Types
- Validation Rules
- Conditional Logic
- Multi-Step Forms
- Container Fields
- Custom Styling
- Events & Actions
- Zustand Store API
- TypeScript Types
- Complete JSON Example
- Extracting Form Values
Installation
npm install @enerjisaformlibrary/formbuilder-reactPeer Dependencies:
react: ^18.0.0react-dom: ^18.0.0
Quick Start
import { FormBuilder, useFormStore } from '@enerjisaformlibrary/formbuilder-react';
import '@enerjisaformlibrary/formbuilder-react/styles.css';
function App() {
const handleChange = (form) => {
// Called on every form schema change
console.log('Form schema updated:', form);
};
const handleSave = (form) => {
// Save form JSON to your database
const jsonString = JSON.stringify(form, null, 2);
saveToDatabase(jsonString);
};
return (
<div style={{ height: '100vh', width: '100%' }}>
<FormBuilder
onChange={handleChange}
onSave={handleSave}
theme="light"
showToolbar={true}
showComponentLibrary={true}
showPropertiesPanel={true}
/>
</div>
);
}Loading Existing Form
import { FormBuilder } from '@enerjisaformlibrary/formbuilder-react';
import '@enerjisaformlibrary/formbuilder-react/styles.css';
function EditForm({ existingFormJson }) {
// Parse JSON string if needed
const initialForm = typeof existingFormJson === 'string'
? JSON.parse(existingFormJson)
: existingFormJson;
return (
<FormBuilder
initialForm={initialForm}
onSave={(form) => updateInDatabase(form)}
/>
);
}FormBuilder Props
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| initialForm | FormSchema | undefined | Pre-existing form schema to load |
| onSave | (form: FormSchema) => void | undefined | Callback when save button clicked |
| onChange | (form: FormSchema) => void | undefined | Callback on every form change |
| className | string | '' | Additional CSS class for container |
| theme | 'light' \| 'dark' | 'light' | Color theme |
| showToolbar | boolean | true | Show/hide top toolbar |
| showComponentLibrary | boolean | true | Show/hide left component panel |
| showPropertiesPanel | boolean | true | Show/hide right properties panel |
Exported Components
// Main wrapper component (recommended)
import { FormBuilder } from '@enerjisaformlibrary/formbuilder-react';
// Individual components for custom layouts
import {
FormCanvas, // Central form editing canvas
PropertiesPanel, // Right-side property editor
ComponentLibrary, // Left-side component palette
Toolbar, // Top toolbar with actions
CanvasField, // Individual field renderer
DragOverlayContent, // Drag preview overlay
JsonViewerModal, // JSON preview modal
} from '@enerjisaformlibrary/formbuilder-react';
// Zustand store for state management
import { useFormStore } from '@enerjisaformlibrary/formbuilder-react';
// All TypeScript types and schemas
import type {
FormSchema,
FormField,
FormRow,
FormColumn,
Step,
FieldProps,
Validation,
ConditionalLogic,
CustomStyle,
SubmissionConfig,
} from '@enerjisaformlibrary/formbuilder-react';Form Schema Structure
The form is represented as a hierarchical JSON structure:
interface FormSchema {
id: string; // Unique form identifier
name: string; // Form display name
description?: string; // Optional description
rows: FormRow[]; // Array of rows (single-page mode)
steps?: Step[]; // Array of steps (multi-step mode)
isMultiStep?: boolean; // Enable multi-step wizard
submissionConfig?: SubmissionConfig; // Webhook/submission settings
settings?: FormSettings; // Global form settings
versions?: FormVersion[]; // Version history
currentVersion?: number; // Current version number
createdAt?: string; // ISO date string
updatedAt?: string; // ISO date string
}
interface FormRow {
id: string;
columns: FormColumn[]; // 12-column grid system
conditionalLogic?: ConditionalLogic; // Row-level conditions
fieldSize?: 'compact' | 'normal' | 'comfortable';
spacing?: number; // 0-16
padding?: number; // 0-16
}
interface FormColumn {
id: string;
width: number; // 1-12 (grid columns)
responsiveWidth?: ResponsiveWidth; // Mobile/tablet overrides
fields: FormField[]; // Fields in this column
}
interface FormField {
id: string; // Unique field identifier
type: string; // Field type (see Field Types)
props: FieldProps; // Field properties
validation?: Validation; // Validation rules
conditionalLogic?: ConditionalLogic; // Field conditions
customStyle?: CustomStyle; // CSS customization
localization?: Localization; // Multi-language support
computed?: ComputedField; // Calculated fields
responsiveWidth?: ResponsiveWidth; // Responsive breakpoints
events?: FieldEvents; // Event handlers
}Field Types
Basic Input Fields
| Type | Description | Key Props |
|------|-------------|-----------|
| input | Single-line text input | placeholder, maxLength |
| textarea | Multi-line text area | placeholder, rows |
| number | Numeric input | min, max, step |
| email | Email input with validation | placeholder |
| password | Password input (masked) | placeholder |
| phone | Phone number input | placeholder |
| url | URL input with validation | placeholder |
Selection Fields
| Type | Description | Key Props |
|------|-------------|-----------|
| dropdown | Single select dropdown | options, optionsString |
| checkbox | Single checkbox | defaultValue |
| radio | Radio button group | options, optionsString |
| toggle | On/off toggle switch | defaultValue |
| multiselect | Multi-select with tags | options, multiSelectConfig |
Date & Time Fields
| Type | Description | Key Props |
|------|-------------|-----------|
| date | Date picker | defaultValue |
| time | Time picker | timeConfig.format |
| daterange | Date range picker | dateRangeConfig |
Advanced Fields
| Type | Description | Key Props |
|------|-------------|-----------|
| file | File upload with drag-drop | fileConfig |
| signature | Signature pad (canvas) | signatureConfig |
| rating | Star rating | ratingConfig.maxRating |
| richtext | Rich text editor | richTextConfig |
| autocomplete | Autocomplete input | autocompleteConfig, options |
| slider | Range slider | min, max, step |
| pattern | Input masking | patternConfig.format |
| qrcode | QR code display | qrCodeConfig.value, qrCodeConfig.size |
| table | Data table input | tableConfig |
| color | Color picker | defaultValue |
Static/Display Elements
| Type | Description | Key Props |
|------|-------------|-----------|
| header | H1 heading | label |
| subheader | H2/H3 heading | label |
| label | Text label | label |
| paragraph | Paragraph text | label |
| divider | Horizontal divider | dividerMargin |
| spacer | Vertical spacer | spacerHeight |
| image | Image display | imageUrl, imageWidth, imageHeight |
| button | Action button | buttonConfig |
Structure Elements
| Type | Description | Key Props |
|------|-------------|-----------|
| container | Grouping container | containerConfig |
| row | Grid row | - |
Field Props (FieldProps)
interface FieldProps {
key: string; // Unique field key (for form values)
label: string; // Display label
placeholder?: string; // Input placeholder
value?: any; // Current value
defaultValue?: any; // Default value
helpText?: string; // Help text below field
tooltip?: string; // Tooltip on hover
size?: 'small' | 'medium' | 'large';
containerWidth?: 'auto' | '25' | '33' | '50' | '66' | '75' | '100';
disabled?: boolean; // Disable field
readOnly?: boolean; // Read-only mode
hidden?: boolean; // Hide field
autoFocus?: boolean; // Auto-focus on mount
tabIndex?: number; // Tab order
htmlAttributes?: Record<string, string>; // Custom HTML attributes
// Options for selection fields
options?: Array<{ label: string; value: string }>;
optionsString?: string[]; // Simple string array for options
// Field-specific configs
fileConfig?: FileUploadConfig;
ratingConfig?: RatingConfig;
autocompleteConfig?: AutocompleteConfig;
richTextConfig?: RichTextConfig;
dateRangeConfig?: DateRangeConfig;
timeConfig?: TimeConfig;
signatureConfig?: SignatureConfig;
multiSelectConfig?: MultiSelectConfig;
patternConfig?: PatternConfig;
buttonConfig?: ButtonConfig;
qrCodeConfig?: QRCodeConfig;
containerConfig?: ContainerConfig;
tableConfig?: TableConfig;
// Layout
spacerHeight?: number; // For spacer type
dividerMargin?: number; // For divider type
imageUrl?: string; // For image type
imageWidth?: string;
imageHeight?: string;
}Validation Rules
interface Validation {
required?: boolean; // Field is required
minLength?: number; // Minimum text length
maxLength?: number; // Maximum text length
min?: number; // Minimum numeric value
max?: number; // Maximum numeric value
pattern?: string; // Regex pattern
errorMessage?: string; // Custom error message
customValidation?: string; // Custom validation code
validationType?: 'email' | 'url' | 'phone' | 'creditCard' | 'custom';
autoValidate?: boolean; // Validate automatically
validateOnBlur?: boolean; // Validate on blur
validateOnChange?: boolean; // Validate on change
}Example:
{
"validation": {
"required": true,
"minLength": 3,
"maxLength": 100,
"pattern": "^[a-zA-Z]+$",
"errorMessage": "Only letters allowed, 3-100 characters"
}
}Conditional Logic
Show, hide, enable, disable, or require fields based on other field values:
interface ConditionalLogic {
enabled: boolean; // Enable/disable conditional logic
action: 'show' | 'hide' | 'enable' | 'disable' | 'require';
logicType: 'all' | 'any'; // Match all or any condition
conditions: Condition[];
}
interface Condition {
fieldKey: string; // Key of field to check
operator: ConditionOperator; // Comparison operator
value?: any; // Value to compare against
}
type ConditionOperator =
| 'equals'
| 'notEquals'
| 'contains'
| 'notContains'
| 'greaterThan'
| 'lessThan'
| 'greaterThanOrEqual'
| 'lessThanOrEqual'
| 'isEmpty'
| 'isNotEmpty'
| 'startsWith'
| 'endsWith';Example: Show field when another field equals "yes":
{
"conditionalLogic": {
"enabled": true,
"action": "show",
"logicType": "all",
"conditions": [
{
"fieldKey": "has_experience",
"operator": "equals",
"value": "yes"
}
]
}
}Multi-Step Forms
Enable wizard-style forms with multiple steps:
interface Step {
id: string;
title: string; // Step title
description?: string; // Step description
icon?: string; // Lucide icon name
rows: FormRow[]; // Rows in this step
validation?: {
validateOnNext?: boolean; // Validate before next step
allowSkip?: boolean; // Allow skipping this step
};
}Example Multi-Step Form:
{
"id": "registration-form",
"name": "User Registration",
"isMultiStep": true,
"steps": [
{
"id": "step-1",
"title": "Personal Info",
"description": "Enter your personal details",
"rows": [...]
},
{
"id": "step-2",
"title": "Contact",
"description": "How can we reach you?",
"rows": [...]
},
{
"id": "step-3",
"title": "Preferences",
"validation": { "allowSkip": true },
"rows": [...]
}
],
"rows": []
}Container Fields
Containers allow nested layouts with their own grid system:
interface ContainerConfig {
direction?: 'row' | 'column';
gap?: number; // Gap between items
padding?: number; // Internal padding
background?: string; // Background color
border?: boolean; // Show border
flexWrap?: 'wrap' | 'nowrap';
justifyContent?: 'flex-start' | 'flex-end' | 'center' | 'space-between' | 'space-around' | 'space-evenly';
alignItems?: 'flex-start' | 'flex-end' | 'center' | 'stretch' | 'baseline';
fieldSize?: 'compact' | 'normal' | 'comfortable';
rows?: ContainerRow[]; // Nested rows structure
}
interface ContainerRow {
id: string;
width: number; // 1-12 grid width
height?: number; // 40-500 pixels or auto
columns: ContainerColumn[];
}
interface ContainerColumn {
id: string;
width: number; // 1-12 grid width
fieldId?: string; // ID of field in this column
fields?: FormField[]; // Fields in this column
}Custom Styling
Per-field CSS customization:
interface CustomStyle {
className?: string; // Additional CSS class
labelClassName?: string; // CSS class for label
inputClassName?: string; // CSS class for input
containerClassName?: string; // CSS class for container
css?: string; // Custom inline CSS
wrapperCss?: string; // Wrapper element CSS
deviceTarget?: 'any' | 'mobile' | 'tablet' | 'desktop';
width?: { value?: number; unit?: string };
height?: string;
marginTop?: string;
marginRight?: string;
marginBottom?: string;
marginLeft?: string;
color?: string;
backgroundColor?: string;
}Example:
{
"customStyle": {
"containerClassName": "my-field-wrapper",
"labelClassName": "text-lg font-bold",
"inputClassName": "border-2 border-blue-500",
"backgroundColor": "#f0f0f0"
}
}Events & Actions
Define event handlers for field interactions:
interface FieldEvents {
onClick?: EventAction[];
onChange?: EventAction[];
onFocus?: EventAction[];
onBlur?: EventAction[];
onMount?: EventAction[];
onUnmount?: EventAction[];
}
interface EventAction {
type: 'common' | 'code' | 'custom';
name: string; // Action name
code?: string; // JavaScript code (for code type)
args?: Record<string, any>; // Action arguments
}Submission Config
Configure form submission behavior:
interface SubmissionConfig {
enabled: boolean;
webhookUrl?: string; // Webhook endpoint
webhookMethod?: 'POST' | 'PUT' | 'PATCH';
webhookHeaders?: Record<string, string>;
emailNotification?: {
enabled?: boolean;
to?: string[];
subject?: string;
template?: string;
};
apiEndpoint?: {
url?: string;
method?: 'POST' | 'PUT' | 'PATCH';
headers?: Record<string, string>;
};
redirectUrl?: string; // Redirect after submit
successMessage?: string; // Success message
errorMessage?: string; // Error message
}Zustand Store API
Access and manipulate form state programmatically:
import { useFormStore } from '@enerjisaformlibrary/formbuilder-react';
function MyComponent() {
const {
// State
form, // Current FormSchema
selection, // Selected element
previewMode, // Preview mode state
formValues, // Form values (in preview)
// Form Actions
loadForm, // Load form schema
setFormName, // Set form name
resetForm, // Reset to empty form
// Row Actions
addRow, // Add empty row
addRowWithField, // Add row with a field
updateRow, // Update row properties
deleteRow, // Delete row
moveRow, // Move/reorder row
// Column Actions
addColumn, // Add column to row
updateColumn, // Update column properties
deleteColumn, // Delete column
// Field Actions
addField, // Add field to column
updateField, // Update field properties
deleteField, // Delete field
moveField, // Move field between columns
// Container Actions
addFieldToContainer, // Add field to container column
addFieldToContainerDirect, // Add field to container
// Selection
setSelection, // Set selected element
clearSelection, // Clear selection
// Preview
setPreviewMode, // Toggle preview mode
setFormValue, // Set field value in preview
// Multi-step
addStep, // Add wizard step
updateStep, // Update step
deleteStep, // Delete step
moveStep, // Reorder step
setCurrentStep, // Navigate to step
// Version Control
saveVersion, // Save form version
restoreVersion, // Restore previous version
// Undo/Redo
undo, // Undo last action
redo, // Redo undone action
canUndo, // Check if undo available
canRedo, // Check if redo available
} = useFormStore();
// Example: Get current form JSON
const formJson = JSON.stringify(form, null, 2);
// Example: Load existing form
useEffect(() => {
loadForm(existingFormData);
}, []);
}TypeScript Types
All types are exported for TypeScript usage:
import type {
// Core Types
FormSchema,
FormField,
FormRow,
FormColumn,
Step,
// Field Configuration
FieldProps,
Validation,
ConditionalLogic,
Condition,
ConditionOperator,
CustomStyle,
ResponsiveWidth,
FieldEvents,
EventAction,
// Field-Specific Configs
FileUploadConfig,
RatingConfig,
AutocompleteConfig,
SignatureConfig,
PatternConfig,
ButtonConfig,
QRCodeConfig,
ContainerConfig,
TableConfig,
// Form Settings
FormSettings,
SubmissionConfig,
FormVersion,
Localization,
ComputedField,
// Selection
Selection,
SelectionType,
// Field Type Constants
FieldType,
StructureType,
StaticType,
} from '@enerjisaformlibrary/formbuilder-react';Complete JSON Example
Here's a complete form with various field types:
{
"id": "contact-form-001",
"name": "Contact Form",
"description": "A sample contact form",
"isMultiStep": false,
"rows": [
{
"id": "row-1",
"columns": [
{
"id": "col-1",
"width": 6,
"fields": [
{
"id": "field-1",
"type": "input",
"props": {
"key": "first_name",
"label": "First Name",
"placeholder": "Enter your first name"
},
"validation": {
"required": true,
"minLength": 2
}
}
]
},
{
"id": "col-2",
"width": 6,
"fields": [
{
"id": "field-2",
"type": "input",
"props": {
"key": "last_name",
"label": "Last Name",
"placeholder": "Enter your last name"
},
"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]"
},
"validation": {
"required": true,
"validationType": "email"
}
}
]
}
]
},
{
"id": "row-3",
"columns": [
{
"id": "col-4",
"width": 12,
"fields": [
{
"id": "field-4",
"type": "dropdown",
"props": {
"key": "inquiry_type",
"label": "Inquiry Type",
"options": [
{ "label": "General Question", "value": "general" },
{ "label": "Technical Support", "value": "support" },
{ "label": "Sales", "value": "sales" },
{ "label": "Other", "value": "other" }
]
},
"validation": {
"required": true
}
}
]
}
]
},
{
"id": "row-4",
"columns": [
{
"id": "col-5",
"width": 12,
"fields": [
{
"id": "field-5",
"type": "textarea",
"props": {
"key": "message",
"label": "Message",
"placeholder": "Type your message here..."
},
"validation": {
"required": true,
"minLength": 10,
"maxLength": 1000
},
"conditionalLogic": {
"enabled": true,
"action": "show",
"logicType": "all",
"conditions": [
{
"fieldKey": "inquiry_type",
"operator": "isNotEmpty"
}
]
}
}
]
}
]
},
{
"id": "row-5",
"columns": [
{
"id": "col-6",
"width": 12,
"fields": [
{
"id": "field-6",
"type": "checkbox",
"props": {
"key": "agree_terms",
"label": "I agree to the terms and conditions"
},
"validation": {
"required": true,
"errorMessage": "You must agree to continue"
}
}
]
}
]
},
{
"id": "row-6",
"columns": [
{
"id": "col-7",
"width": 12,
"fields": [
{
"id": "field-7",
"type": "button",
"props": {
"key": "submit_btn",
"label": "Submit",
"buttonConfig": {
"action": "submit",
"variant": "default"
}
}
}
]
}
]
}
],
"submissionConfig": {
"enabled": true,
"webhookUrl": "https://api.example.com/forms/submit",
"webhookMethod": "POST",
"successMessage": "Thank you! Your message has been sent.",
"redirectUrl": "/thank-you"
},
"settings": {
"defaultLanguage": "en",
"layout": {
"labelPosition": "top",
"spacing": "normal"
}
}
}Extracting Form Values
When rendering this form to end users, you'll need to:
- Parse the JSON to get field structure
- Render fields based on their
type - Collect values using field
props.keyas the key - Apply validation from
validationobject - Handle conditional logic to show/hide fields
Expected form values output:
{
"first_name": "John",
"last_name": "Doe",
"email": "[email protected]",
"inquiry_type": "support",
"message": "I need help with...",
"agree_terms": true
}Iterating through fields:
function extractFieldsFromForm(form: FormSchema): FormField[] {
const fields: FormField[] = [];
const processRows = (rows: FormRow[]) => {
for (const row of rows) {
for (const column of row.columns) {
for (const field of column.fields) {
fields.push(field);
// Handle container fields (nested)
if (field.type === 'container' && field.props.containerConfig?.rows) {
for (const containerRow of field.props.containerConfig.rows) {
for (const containerCol of containerRow.columns) {
if (containerCol.fields) {
fields.push(...containerCol.fields);
}
}
}
}
}
}
}
};
if (form.isMultiStep && form.steps) {
for (const step of form.steps) {
processRows(step.rows);
}
} else {
processRows(form.rows);
}
return fields;
}
// Get field keys for form values
const fields = extractFieldsFromForm(formSchema);
const fieldKeys = fields
.filter(f => !['header', 'subheader', 'label', 'paragraph', 'divider', 'spacer', 'image', 'button'].includes(f.type))
.map(f => f.props.key);Responsive Width
Fields support responsive grid widths:
interface ResponsiveWidth {
mobile?: number; // 1-12 columns on mobile
tablet?: number; // 1-12 columns on tablet
desktop?: number; // 1-12 columns on desktop
}Example:
{
"responsiveWidth": {
"mobile": 12,
"tablet": 6,
"desktop": 4
}
}Version History
The form supports versioning:
interface FormVersion {
id: string;
version: number;
createdAt: string; // ISO date
createdBy?: string; // User identifier
changelog?: string; // Description of changes
snapshot: FormSchema; // Complete form state
}Localization
Multi-language support per field:
interface Localization {
translations?: Record<string, {
label?: string;
placeholder?: string;
helpText?: string;
errorMessages?: Record<string, string>;
}>;
}Example:
{
"localization": {
"translations": {
"tr": {
"label": "Ad",
"placeholder": "Adınızı girin"
},
"de": {
"label": "Vorname",
"placeholder": "Geben Sie Ihren Vornamen ein"
}
}
}
}Supported Languages
const SUPPORTED_LANGUAGES = [
{ code: 'en', name: 'English' },
{ code: 'tr', name: 'Turkish' },
{ code: 'de', name: 'German' },
{ code: 'fr', name: 'French' },
{ code: 'es', name: 'Spanish' },
{ code: 'pt', name: 'Portuguese' },
{ code: 'it', name: 'Italian' },
{ code: 'nl', name: 'Dutch' },
{ code: 'ru', name: 'Russian' },
{ code: 'zh', name: 'Chinese' },
{ code: 'ja', name: 'Japanese' },
{ code: 'ko', name: 'Korean' },
{ code: 'ar', name: 'Arabic' },
];License
MIT
