npm package: @sandesh4ever/react-dynamic-form-builder
Description: A React package for creating dynamic forms from JSON using Formik and Yup
Author: sandesh4ever
Latest version: 1.1.0

# React Dynamic Form Builder

A React package for creating dynamic forms from JSON using Formik and Yup validation. Easily generate forms with validation, custom styling, and multiple field types from a simple JSON configuration.

## Installation

```bash
npm install @sandesh4ever/react-dynamic-form-builder
```

**Package Published**: ✅ Available on npm at https://www.npmjs.com/package/@sandesh4ever/react-dynamic-form-builder

## 🎮 Interactive Playground

Try out all features in our interactive playground:

```bash
# Install playground dependencies
npm run demo:install

# Start the playground
npm run demo
```

The playground will open at [http://localhost:3000](http://localhost:3000) and includes:

- Live form preview with all field types
- Template selector (Custom, Registration, Contact, Address, Multi-Step)
- Form controls (Reset, Validate, Set Values, Get Values, Submit)
- Real-time form values display
- All features demonstrated with examples

See `demo/README.md` for more details.

## Peer Dependencies

Make sure you have these installed in your project:

```bash
npm install react react-dom formik yup
```

**Optional:** If you want to use the Material-UI renderer:

```bash
npm install @mui/material @emotion/react @emotion/styled
```

## Quick Start

```jsx
import React from "react";
import { DynamicForm } from "@sandesh4ever/react-dynamic-form-builder";

const formConfig = {
  title: "User Registration",
  submitLabel: "Register",
  fields: [
    {
      name: "firstName",
      type: "text",
      label: "First Name",
      placeholder: "Enter your first name",
      required: true,
      minLength: 2,
      maxLength: 50,
    },
    {
      name: "email",
      type: "email",
      label: "Email Address",
      placeholder: "Enter your email",
      required: true,
    },
    {
      name: "password",
      type: "password",
      label: "Password",
      placeholder: "Enter your password",
      required: true,
      minLength: 8,
      pattern: "^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d).+$",
      patternMessage: "Password must contain uppercase, lowercase, and number",
    },
    {
      name: "confirmPassword",
      type: "password",
      label: "Confirm Password",
      placeholder: "Confirm your password",
      required: true,
      dependsOn: {
        field: "password",
        condition: "equals",
        matchField: true,
        errorMessage: "Passwords must match",
      },
    },
  ],
};

function App() {
  const handleSubmit = (values, actions) => {
    console.log("Form values:", values);
    // Handle form submission
    actions.setSubmitting(false);
  };

  return <DynamicForm formConfig={formConfig} onSubmit={handleSubmit} />;
}

export default App;
```

## Features

- ✅ Dynamic form generation from JSON
- ✅ Full Formik integration for form state management
- ✅ Yup validation with customizable rules
- ✅ Custom CSS styling support
- ✅ **15+ Field Types** (text, email, file, switch, rating, tags, etc.)
- ✅ **Multi-step/Wizard Forms** - Break forms into multiple steps
- ✅ **Field Groups/Sections** - Organize fields into collapsible sections
- ✅ **File Upload** - Single and multiple file uploads with previews
- ✅ **Grid Layout System** - Responsive grid layouts for fields
- ✅ **Array/Repeatable Fields** - Dynamic lists of fields
- ✅ **Nested Objects** - Support for nested form structures
- ✅ **Custom Validation** - Async and sync custom validators
- ✅ **Conditional Validation** - Validation rules based on other fields
- ✅ **Form Callbacks** - onChange, onBlur, onStepChange handlers
- ✅ **Preview Mode** - Read-only form display
- ✅ **Help Text & Descriptions** - Additional guidance for fields
- ✅ **Form Templates** - Pre-built form configurations
- ✅ **Form State Management** - Programmatic control via refs
- ✅ **API-based Options** - Dynamic dropdown options from API
- ✅ **Field Dependencies** - Show/hide/disable fields conditionally
- ✅ **Date Range Validation** - Start/end date dependencies
- ✅ **Accessibility** - ARIA labels, keyboard navigation support
- ✅ Custom field renderers
- ✅ TypeScript-friendly API

## Field Types Supported

### Basic Input Types

- `text` - Text input
- `email` - Email input with validation
- `password` - Password input (supports confirmation matching)
- `number` - Number input
- `tel` - Telephone input
- `url` - URL input

### Date & Time Types

- `date` - Date input
- `time` - Time input
- `datetime-local` - Date and time input
- `month` - Month input
- `week` - Week input

### Advanced Input Types

- `textarea` - Multi-line text input
- `color` - Color picker
- `range` - Range slider (shows current value)
- `file` - File upload (single or multiple with preview)
- `switch` - Toggle switch (boolean)
- `rating` - Star rating (1-5 or custom max)
- `tags` - Tag input (add/remove tags)
- `search` / `autocomplete` - Search input with dropdown suggestions

### Selection Types

- `select` - Dropdown select (supports API-based options)
- `radio` - Radio button group
- `checkbox` - Checkbox input

### Structural Types

- `section` - Collapsible field group/section
- `object` - Nested object container
- `array` - Repeatable array of fields

## Form Configuration

### Basic Structure

```jsx
const formConfig = {
  title: "Form Title", // Optional
  submitLabel: "Submit", // Optional, defaults to "Submit"
  fields: [
    // Field configurations
  ],
};
```

### Field Configuration

Each field in the `fields` array supports the following properties:

| Property                | Type           | Required | Description                                       |
| ----------------------- | -------------- | -------- | ------------------------------------------------- |
| `name`                  | string         | Yes      | Field name (must be unique)                       |
| `type`                  | string         | Yes      | Field type (text, email, select, etc.)            |
| `label`                 | string         | Yes      | Field label text                                  |
| `placeholder`           | string         | No       | Placeholder text                                  |
| `required`              | boolean        | No       | Whether field is required                         |
| `defaultValue`          | any            | No       | Default field value                               |
| `minLength`             | number         | No       | Minimum length (for text fields)                  |
| `maxLength`             | number         | No       | Maximum length (for text fields)                  |
| `min`                   | number         | No       | Minimum value (for number/range/date fields)      |
| `max`                   | number         | No       | Maximum value (for number/range/date fields)      |
| `step`                  | number         | No       | Step value (for number/range/time fields)         |
| `pattern`               | string         | No       | Regex pattern for validation                      |
| `patternMessage`        | string         | No       | Custom error message for pattern                  |
| `rows`                  | number         | No       | Number of rows (for textarea)                     |
| `options`               | array/function | No       | Options for select/radio fields or async function |
| `dependsOn`             | object         | No       | Field dependency configuration (see below)        |
| `helpText`              | string         | No       | Help text displayed below field                   |
| `description`           | string         | No       | Detailed description displayed below field        |
| `colSpan`               | number         | No       | Grid column span (1-12, for grid layouts)         |
| `disabled`              | boolean        | No       | Whether field is disabled                         |
| `accept`                | string         | No       | File types accepted (for file fields)             |
| `multiple`              | boolean        | No       | Allow multiple values (file/select/array)         |
| `customValidation`      | function       | No       | Custom validation function                        |
| `customValidationError` | string         | No       | Error message for custom validation               |
| `validateWhen`          | object         | No       | Conditional validation rules                      |
| `itemLabel`             | string         | No       | Label for array items                             |
| `size`                  | number         | No       | Size for rating stars                             |

### Options Array (for select/radio)

**Static Options:**

```jsx
options: [
  { value: "value1", label: "Label 1" },
  { value: "value2", label: "Label 2" },
];
```

**Dynamic Options from API (Function):**

```jsx
options: async (formValues) => {
  // formValues contains all current form values
  // Make API call to fetch options
  const response = await fetch("/api/options");
  const data = await response.json();

  // Return array of options
  return data.map((item) => ({
    value: item.id,
    label: item.name,
  }));
};
```

**Dynamic Options with Dependencies:**

```jsx
// Options depend on another field value (e.g., states based on country)
options: async (formValues) => {
  const countryId = formValues.country; // Get value from dependent field

  if (!countryId) {
    return []; // Return empty if dependency not met
  }

  // Make API call with dependency value
  const response = await fetch(`/api/states?country=${countryId}`);
  const data = await response.json();

  return data.map((state) => ({
    value: state.id,
    label: state.name,
  }));
};
```

### Field Dependencies

Fields can depend on other fields to control visibility and validation. This is especially useful for password confirmation and conditional fields.

```jsx
dependsOn: {
  field: "fieldName",        // Name of the field this depends on
  condition: "equals",       // Condition type (see conditions below)
  value: "someValue",        // Value to compare (not needed for matchField)
  matchField: true,          // If true, field value must match the dependency field (for password confirmation)
  errorMessage: "Custom error message",
  fieldLabel: "Password",    // Label for error messages
  disableBehavior: "disable" // "disable" (default) - disable when condition not met, "hide" - hide when condition not met
}
```

#### Dependency Conditions

- `equals` - Field is visible/valid when dependency equals value (or matches field if matchField is true)
- `notEquals` - Field is visible when dependency does not equal value
- `contains` - Field is visible when dependency contains value (string)
- `notContains` - Field is visible when dependency does not contain value
- `greaterThan` - Field is visible/valid when dependency is greater than value (numbers) or field value must be greater (dates)
- `lessThan` - Field is visible/valid when dependency is less than value (numbers) or field value must be less (dates)
- `greaterThanOrEqual` - Field is visible when dependency >= value
- `lessThanOrEqual` - Field is visible when dependency <= value
- `afterDate` - Field value must be after dependency field (for dates - ensures end date > start date)
- `beforeDate` - Field value must be before dependency field (for dates - ensures start date < end date)
- `isEmpty` - Field is visible when dependency is empty
- `isNotEmpty` - Field is visible when dependency is not empty
- `isTrue` - Field is visible/enabled when dependency is true (for checkboxes)
- `isFalse` - Field is visible/enabled when dependency is false (for checkboxes)

#### Disable Behavior Options

- `"disable"` (default) - Field is disabled (grayed out, not editable) when condition is NOT met, but remains visible
- `"hide"` - Field is completely hidden when condition is NOT met (not rendered in DOM)

**Note:** When a field is disabled, it becomes optional in validation (won't fail required validation if empty).

## Complete Example

```jsx
import React from "react";
import { DynamicForm } from "@sandesh4ever/react-dynamic-form-builder";

const formConfig = {
  title: "Complete Registration Form",
  submitLabel: "Register",
  fields: [
    {
      name: "firstName",
      type: "text",
      label: "First Name",
      placeholder: "Enter your first name",
      required: true,
      minLength: 2,
      maxLength: 50,
    },
    {
      name: "lastName",
      type: "text",
      label: "Last Name",
      placeholder: "Enter your last name",
      required: true,
      minLength: 2,
      maxLength: 50,
    },
    {
      name: "email",
      type: "email",
      label: "Email Address",
      placeholder: "your@email.com",
      required: true,
    },
    {
      name: "phone",
      type: "tel",
      label: "Phone Number",
      placeholder: "+1 (555) 123-4567",
      required: true,
      pattern: "^[+]?[(]?[0-9]{3}[)]?[-\\s.]?[0-9]{3}[-\\s.]?[0-9]{4,6}$",
      patternMessage: "Please enter a valid phone number",
    },
    {
      name: "password",
      type: "password",
      label: "Password",
      placeholder: "Enter your password",
      required: true,
      minLength: 8,
      pattern: "^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d).+$",
      patternMessage: "Password must contain uppercase, lowercase, and number",
    },
    {
      name: "confirmPassword",
      type: "password",
      label: "Confirm Password",
      placeholder: "Confirm your password",
      required: true,
      dependsOn: {
        field: "password",
        condition: "equals",
        matchField: true,
        errorMessage: "Passwords must match",
        fieldLabel: "Password",
      },
    },
    {
      name: "birthDate",
      type: "date",
      label: "Birth Date",
      required: true,
      max: new Date().toISOString().split("T")[0], // Cannot be in future
    },
    {
      name: "projectStartDate",
      type: "date",
      label: "Project Start Date",
      required: true,
      min: new Date().toISOString().split("T")[0], // Cannot be in the past
    },
    {
      name: "projectEndDate",
      type: "date",
      label: "Project End Date",
      required: true,
      // End date must be after start date
      dependsOn: {
        field: "projectStartDate",
        condition: "afterDate",
        errorMessage: "Project end date must be after start date",
      },
    },
    {
      name: "appointmentTime",
      type: "time",
      label: "Preferred Appointment Time",
      required: false,
    },
    {
      name: "age",
      type: "number",
      label: "Age",
      required: true,
      min: 18,
      max: 100,
      step: 1,
    },
    {
      name: "volume",
      type: "range",
      label: "Volume",
      min: 0,
      max: 100,
      step: 5,
      defaultValue: 50,
    },
    {
      name: "favoriteColor",
      type: "color",
      label: "Favorite Color",
      defaultValue: "#000000",
    },
    {
      name: "country",
      type: "select",
      label: "Country",
      required: true,
      options: [
        { value: "us", label: "United States" },
        { value: "uk", label: "United Kingdom" },
        { value: "ca", label: "Canada" },
        { value: "au", label: "Australia" },
        { value: "other", label: "Other" },
      ],
    },
    {
      name: "state",
      type: "select",
      label: "State/Province",
      required: false,
      dependsOn: {
        field: "country",
        condition: "equals",
        value: "us",
      },
      options: [
        { value: "ny", label: "New York" },
        { value: "ca", label: "California" },
        { value: "tx", label: "Texas" },
      ],
    },
    {
      name: "gender",
      type: "radio",
      label: "Gender",
      required: true,
      options: [
        { value: "male", label: "Male" },
        { value: "female", label: "Female" },
        { value: "other", label: "Other" },
        { value: "prefer-not-to-say", label: "Prefer not to say" },
      ],
    },
    {
      name: "newsletter",
      type: "checkbox",
      label: "Subscribe to newsletter",
      defaultValue: false,
    },
    {
      name: "termsAccepted",
      type: "checkbox",
      label: "I agree to the terms and conditions",
      required: true,
      defaultValue: false,
    },
    {
      name: "preferredContact",
      type: "radio",
      label: "Preferred Contact Method",
      required: true,
      options: [
        { value: "email", label: "Email" },
        { value: "phone", label: "Phone" },
        { value: "sms", label: "SMS" },
      ],
    },
    {
      name: "phoneNumberForContact",
      type: "tel",
      label: "Phone Number",
      placeholder: "Enter phone number for contact",
      required: true,
      dependsOn: {
        field: "preferredContact",
        condition: "contains",
        value: "phone",
      },
    },
    {
      name: "bio",
      type: "textarea",
      label: "Bio",
      placeholder: "Tell us about yourself",
      required: false,
      rows: 5,
      maxLength: 500,
    },
    {
      name: "website",
      type: "url",
      label: "Website",
      placeholder: "https://example.com",
      required: false,
    },
  ],
};

function App() {
  const handleSubmit = (values, actions) => {
    console.log("Form submitted:", values);

    // Simulate API call
    setTimeout(() => {
      alert("Form submitted successfully!");
      actions.setSubmitting(false);
      actions.resetForm();
    }, 1000);
  };

  const initialValues = {
    firstName: "John",
    email: "john@example.com",
  };

  return (
    <div>
      <DynamicForm
        formConfig={formConfig}
        initialValues={initialValues}
        onSubmit={handleSubmit}
      />
    </div>
  );
}

export default App;
```

## New Features Documentation

### 1. Help Text and Descriptions

Add guidance text below fields:

```jsx
{
  name: "email",
  type: "email",
  label: "Email Address",
  required: true,
  helpText: "We'll never share your email with anyone",
  description: "This email will be used for account recovery and notifications"
}
```

### 2. File Upload Support

Single file upload:

```jsx
{
  name: "avatar",
  type: "file",
  label: "Upload Avatar",
  accept: "image/*",
  required: true,
  helpText: "Upload a profile picture (max 5MB)"
}
```

Multiple file upload:

```jsx
{
  name: "documents",
  type: "file",
  label: "Upload Documents",
  multiple: true,
  accept: ".pdf,.doc,.docx",
  required: true
}
```

### 3. Field Groups/Sections

Organize fields into collapsible sections:

```jsx
{
  type: "section",
  name: "personalInfo",
  title: "Personal Information",
  fields: [
    {
      name: "firstName",
      type: "text",
      label: "First Name",
      required: true
    },
    {
      name: "lastName",
      type: "text",
      label: "Last Name",
      required: true
    }
  ]
}
```

### 4. Multi-Step/Wizard Forms

Break forms into multiple steps:

```jsx
const formConfig = {
  title: "Registration Wizard",
  steps: [
    {
      title: "Step 1: Personal Info",
      fields: [{ name: "name", type: "text", label: "Name", required: true }],
    },
    {
      title: "Step 2: Contact Info",
      fields: [
        { name: "email", type: "email", label: "Email", required: true },
      ],
    },
    {
      title: "Step 3: Preferences",
      fields: [
        {
          name: "newsletter",
          type: "checkbox",
          label: "Subscribe to newsletter",
        },
      ],
    },
  ],
};
```

### 5. Custom Validation Functions

Add custom async or sync validation:

```jsx
{
  name: "username",
  type: "text",
  label: "Username",
  required: true,
  customValidation: async (value, formValues) => {
    // Async validation example
    const response = await fetch(`/api/check-username?username=${value}`);
    const data = await response.json();
    if (data.exists) {
      throw new Error("Username already taken");
    }
    return true;
  },
  customValidationError: "Custom error message here"
}
```

Sync validation:

```jsx
{
  name: "age",
  type: "number",
  label: "Age",
  customValidation: (value) => {
    if (value < 18) {
      return "Must be 18 or older";
    }
    return true;
  }
}
```

### 6. New Field Types

#### Switch/Toggle

```jsx
{
  name: "notifications",
  type: "switch",
  label: "Enable Notifications",
  defaultValue: false
}
```

#### Rating

```jsx
{
  name: "satisfaction",
  type: "rating",
  label: "Rate your satisfaction",
  max: 5,
  required: true,
  size: 30 // Size in pixels
}
```

#### Tags

```jsx
{
  name: "skills",
  type: "tags",
  label: "Skills",
  placeholder: "Add skill and press Enter",
  required: true
}
```

#### Search/Autocomplete

```jsx
{
  name: "country",
  type: "search",
  label: "Search Country",
  options: async (formValues) => {
    const response = await fetch('/api/countries');
    const data = await response.json();
    return data.map(c => ({ value: c.code, label: c.name }));
  }
}
```

### 7. Array/Repeatable Fields

Create dynamic lists of fields:

```jsx
{
  name: "addresses",
  type: "array",
  label: "Addresses",
  itemLabel: "Address",
  required: true,
  fields: [
    {
      name: "street",
      type: "text",
      label: "Street Address",
      required: true
    },
    {
      name: "city",
      type: "text",
      label: "City",
      required: true
    },
    {
      name: "zipCode",
      type: "text",
      label: "ZIP Code",
      required: true
    }
  ]
}
```

### 8. Grid/Layout System

Create responsive grid layouts:

```jsx
// Fields automatically use grid when colSpan is specified
{
  name: "firstName",
  type: "text",
  label: "First Name",
  colSpan: 6,  // Takes 6 out of 12 columns (50% width)
  required: true
},
{
  name: "lastName",
  type: "text",
  label: "Last Name",
  colSpan: 6,  // Takes 6 out of 12 columns (50% width)
  required: true
}
```

### 9. Conditional Validation

Validation rules based on other fields:

```jsx
{
  name: "drivingLicense",
  type: "text",
  label: "Driving License Number",
  validateWhen: {
    field: "age",
    condition: "greaterThan",
    value: 16,
    then: { required: true },
    otherwise: { required: false }
  }
}
```

### 10. Form Callbacks

Handle form events:

```jsx
<DynamicForm
  formConfig={formConfig}
  onSubmit={handleSubmit}
  onChange={(values) => {
    console.log("Form values changed:", values);
    // Save to localStorage, etc.
  }}
  onFieldBlur={(fieldName, value) => {
    console.log(`Field ${fieldName} blurred with value:`, value);
  }}
  onStepChange={(stepIndex) => {
    console.log("Step changed to:", stepIndex);
  }}
/>
```

### 11. Preview/Read-Only Mode

Display forms without editing:

```jsx
<DynamicForm
  formConfig={formConfig}
  onSubmit={handleSubmit}
  mode="preview" // Form is read-only
  initialValues={{
    name: "John Doe",
    email: "john@example.com",
  }}
/>
```

### 12. Nested Objects

Handle nested form structures:

```jsx
{
  name: "address",
  type: "object",
  label: "Address",
  fields: [
    {
      name: "street",
      type: "text",
      label: "Street",
      required: true
    },
    {
      name: "city",
      type: "text",
      label: "City",
      required: true
    }
  ]
}
```

### 13. Form State Management with Refs

Programmatic control of the form:

```jsx
import { useRef } from "react";

function App() {
  const formRef = useRef();

  const handleProgrammaticSubmit = () => {
    // Validate form
    formRef.current.validate().then((errors) => {
      if (Object.keys(errors).length === 0) {
        // Submit programmatically
        formRef.current.submit();
      }
    });
  };

  const handleSetValue = () => {
    // Set field value programmatically
    formRef.current.setValue("email", "test@example.com");
  };

  const handleReset = () => {
    // Reset form
    formRef.current.reset();
  };

  return (
    <div>
      <DynamicForm
        ref={formRef}
        formConfig={formConfig}
        onSubmit={handleSubmit}
      />
      <button onClick={handleProgrammaticSubmit}>
        Submit Programmatically
      </button>
      <button onClick={handleSetValue}>Set Email</button>
      <button onClick={handleReset}>Reset Form</button>
    </div>
  );
}
```

Available ref methods:

- `setValue(fieldName, value)` - Set field value
- `getValue(fieldName)` - Get field value
- `getValues()` - Get all form values
- `reset()` - Reset form
- `validate()` - Validate form (returns Promise)
- `setErrors(errors)` - Set form errors
- `setTouched(touched)` - Set touched fields
- `submit()` - Submit form programmatically
- `setStep(step)` - Set current step (for multi-step forms)

### 14. Form Templates

Use pre-built form templates:

```jsx
import {
  DynamicForm,
  formTemplates,
} from "@sandesh4ever/react-dynamic-form-builder";

// Use a template
const formConfig = formTemplates.registrationForm;

// Or customize a template
const customConfig = {
  ...formTemplates.contactForm,
  title: "Custom Contact Form",
};

function App() {
  return <DynamicForm formConfig={customConfig} onSubmit={handleSubmit} />;
}
```

Available templates:

- `registrationForm` - User registration form
- `contactForm` - Contact form with rating
- `surveyForm` - Multi-step survey form
- `addressForm` - Address form with grid layout

## Field Dependencies Examples

### Password Confirmation

The most common use case - matching password fields:

```jsx
{
  name: "password",
  type: "password",
  label: "Password",
  placeholder: "Enter your password",
  required: true,
  minLength: 8
},
{
  name: "confirmPassword",
  type: "password",
  label: "Confirm Password",
  placeholder: "Confirm your password",
  required: true,
  dependsOn: {
    field: "password",
    condition: "equals",
    matchField: true,  // This makes it match the password field value
    errorMessage: "Passwords must match"
  }
}
```

### Conditional Field Visibility

Show a field only when another field has a specific value:

```jsx
{
  name: "country",
  type: "select",
  label: "Country",
  options: [
    { value: "us", label: "United States" },
    { value: "uk", label: "United Kingdom" },
    { value: "other", label: "Other" }
  ]
},
{
  name: "state",
  type: "select",
  label: "State",
  // Only show when country is "us"
  dependsOn: {
    field: "country",
    condition: "equals",
    value: "us"
  },
  options: [
    { value: "ny", label: "New York" },
    { value: "ca", label: "California" }
  ]
}
```

### Conditional Based on Checkbox - Hide Field

```jsx
{
  name: "hasInsurance",
  type: "checkbox",
  label: "Do you have insurance?",
  defaultValue: false
},
{
  name: "insuranceProvider",
  type: "text",
  label: "Insurance Provider",
  // Hide field when checkbox is not checked
  dependsOn: {
    field: "hasInsurance",
    condition: "isTrue",
    disableBehavior: "hide"  // Field will be hidden when checkbox is unchecked
  }
}
```

### Conditional Based on Checkbox - Disable Field

```jsx
{
  name: "agreeToTerms",
  type: "checkbox",
  label: "I agree to the terms and conditions",
  defaultValue: false
},
{
  name: "subscribeNewsletter",
  type: "checkbox",
  label: "Subscribe to newsletter",
  // Disable field when terms are not agreed
  dependsOn: {
    field: "agreeToTerms",
    condition: "isTrue",
    disableBehavior: "disable"  // Field will be disabled (but visible) when checkbox is unchecked
  }
},
{
  name: "emailForNewsletter",
  type: "email",
  label: "Email for newsletter",
  // Disable email field when newsletter subscription is not checked
  dependsOn: {
    field: "subscribeNewsletter",
    condition: "isTrue",
    disableBehavior: "disable"  // Field disabled when checkbox is unchecked
  }
}
```

### Conditional Based on Radio Selection - Hide Field

```jsx
{
  name: "contactMethod",
  type: "radio",
  label: "Preferred Contact Method",
  options: [
    { value: "email", label: "Email" },
    { value: "phone", label: "Phone" }
  ]
},
{
  name: "phoneNumber",
  type: "tel",
  label: "Phone Number",
  // Hide field when phone is not selected
  dependsOn: {
    field: "contactMethod",
    condition: "equals",
    value: "phone",
    disableBehavior: "hide"  // Field hidden when email is selected
  }
}
```

### Conditional Based on Radio Selection - Disable Field

```jsx
{
  name: "paymentMethod",
  type: "radio",
  label: "Payment Method",
  options: [
    { value: "credit", label: "Credit Card" },
    { value: "debit", label: "Debit Card" },
    { value: "cash", label: "Cash" }
  ]
},
{
  name: "cardNumber",
  type: "text",
  label: "Card Number",
  // Disable when cash is selected, enable for credit/debit
  dependsOn: {
    field: "paymentMethod",
    condition: "equals",
    value: "cash",
    disableBehavior: "disable"  // Field disabled when cash is selected
  }
}
```

### Disable vs Hide - Key Differences

**Disable (`disableBehavior: "disable"`):**

- Field remains visible but grayed out
- Cannot be edited when disabled
- Field becomes optional in validation (won't fail if empty)
- Useful for showing context while preventing input

**Hide (`disableBehavior: "hide"`):**

- Field is completely removed from DOM
- Takes up no space when hidden
- Value is cleared when hidden
- Useful for cleaner UI when field is not relevant

### Complete Example: Disable vs Hide

```jsx
{
  name: "hasDiscount",
  type: "checkbox",
  label: "Do you have a discount code?",
  defaultValue: false
},
{
  name: "discountCode",
  type: "text",
  label: "Discount Code",
  // DISABLE field when checkbox is unchecked (visible but grayed out)
  dependsOn: {
    field: "hasDiscount",
    condition: "isTrue",
    disableBehavior: "disable"
  }
},
{
  name: "hasSpecialRequest",
  type: "checkbox",
  label: "Do you have special requests?",
  defaultValue: false
},
{
  name: "specialRequest",
  type: "textarea",
  label: "Special Request Details",
  // HIDE field when checkbox is unchecked (completely removed)
  dependsOn: {
    field: "hasSpecialRequest",
    condition: "isTrue",
    disableBehavior: "hide"
  }
}
```

### Conditional Based on Number/Value Comparison

```jsx
{
  name: "age",
  type: "number",
  label: "Age",
  min: 0,
  max: 120
},
{
  name: "guardianName",
  type: "text",
  label: "Guardian Name",
  // Only show if age is less than 18
  dependsOn: {
    field: "age",
    condition: "lessThan",
    value: 18
  }
}
```

### Start Date and End Date with Dependencies

The most common use case - ensuring end date is after start date:

```jsx
{
  name: "startDate",
  type: "date",
  label: "Start Date",
  required: true,
  min: new Date().toISOString().split("T")[0], // Cannot be in the past
  placeholder: "Select start date"
},
{
  name: "endDate",
  type: "date",
  label: "End Date",
  required: true,
  // End date must be after start date
  dependsOn: {
    field: "startDate",
    condition: "afterDate",
    errorMessage: "End date must be after start date"
  },
  placeholder: "Select end date"
}
```

### Alternative: Using min with ref for Date Ranges

You can also use the `min` property with a reference to another field:

```jsx
{
  name: "startDate",
  type: "date",
  label: "Start Date",
  required: true,
  min: new Date().toISOString().split("T")[0] // Minimum is today
},
{
  name: "endDate",
  type: "date",
  label: "End Date",
  required: true,
  // Reference to startDate - end date must be after start date
  min: "ref:startDate",
  minMessage: "End date must be after start date"
}
```

### Date Range with Minimum Days Between

```jsx
{
  name: "checkIn",
  type: "date",
  label: "Check-in Date",
  required: true,
  min: new Date().toISOString().split("T")[0]
},
{
  name: "checkOut",
  type: "date",
  label: "Check-out Date",
  required: true,
  // Must be after check-in date
  dependsOn: {
    field: "checkIn",
    condition: "afterDate",
    errorMessage: "Check-out date must be after check-in date"
  }
}
```

### DateTime Range (with time)

```jsx
{
  name: "eventStart",
  type: "datetime-local",
  label: "Event Start",
  required: true
},
{
  name: "eventEnd",
  type: "datetime-local",
  label: "Event End",
  required: true,
  // Event end must be after event start
  dependsOn: {
    field: "eventStart",
    condition: "afterDate",
    errorMessage: "Event end must be after event start"
  }
}
```

### Dynamic Options from API - Country and States

This is the most common use case - loading states based on selected country:

```jsx
{
  name: "country",
  type: "select",
  label: "Country",
  required: true,
  // Load countries from API on form load
  options: async (formValues) => {
    try {
      const response = await fetch('/api/countries');
      const data = await response.json();
      return data.map(country => ({
        value: country.code,
        label: country.name
      }));
    } catch (error) {
      console.error('Error fetching countries:', error);
      return [];
    }
  }
},
{
  name: "state",
  type: "select",
  label: "State/Province",
  required: true,
  // Load states based on selected country
  dependsOn: {
    field: "country",
    condition: "isNotEmpty", // Show when country is selected
    disableBehavior: "hide"
  },
  options: async (formValues) => {
    const countryCode = formValues.country;

    // Return empty if country not selected
    if (!countryCode) {
      return [];
    }

    try {
      // API call with country code as parameter
      const response = await fetch(`/api/states?country=${countryCode}`);
      const data = await response.json();
      return data.map(state => ({
        value: state.code,
        label: state.name
      }));
    } catch (error) {
      console.error('Error fetching states:', error);
      return [];
    }
  }
}
```

### Dynamic Options - Multiple Dependencies

```jsx
{
  name: "category",
  type: "select",
  label: "Category",
  options: async () => {
    const response = await fetch('/api/categories');
    const data = await response.json();
    return data.map(cat => ({ value: cat.id, label: cat.name }));
  }
},
{
  name: "subcategory",
  type: "select",
  label: "Subcategory",
  dependsOn: {
    field: "category",
    condition: "isNotEmpty",
    disableBehavior: "hide"
  },
  options: async (formValues) => {
    const categoryId = formValues.category;
    if (!categoryId) return [];

    const response = await fetch(`/api/subcategories?category=${categoryId}`);
    const data = await response.json();
    return data.map(sub => ({ value: sub.id, label: sub.name }));
  }
},
{
  name: "product",
  type: "select",
  label: "Product",
  dependsOn: {
    field: "subcategory",
    condition: "isNotEmpty",
    disableBehavior: "hide"
  },
  options: async (formValues) => {
    const { category, subcategory } = formValues;
    if (!category || !subcategory) return [];

    // Multiple dependencies in API call
    const response = await fetch(
      `/api/products?category=${category}&subcategory=${subcategory}`
    );
    const data = await response.json();
    return data.map(prod => ({ value: prod.id, label: prod.name }));
  }
}
```

### Complete Example: Country, State, City

```jsx
const formConfig = {
  title: "Location Selection",
  fields: [
    {
      name: "country",
      type: "select",
      label: "Country",
      required: true,
      options: async () => {
        const response = await fetch("https://api.example.com/countries");
        const countries = await response.json();
        return countries.map((c) => ({ value: c.id, label: c.name }));
      },
    },
    {
      name: "state",
      type: "select",
      label: "State",
      required: true,
      dependsOn: {
        field: "country",
        condition: "isNotEmpty",
        disableBehavior: "hide",
      },
      options: async (formValues) => {
        if (!formValues.country) return [];

        const response = await fetch(
          `https://api.example.com/states?countryId=${formValues.country}`
        );
        const states = await response.json();
        return states.map((s) => ({ value: s.id, label: s.name }));
      },
    },
    {
      name: "city",
      type: "select",
      label: "City",
      required: true,
      dependsOn: {
        field: "state",
        condition: "isNotEmpty",
        disableBehavior: "hide",
      },
      options: async (formValues) => {
        if (!formValues.state) return [];

        const response = await fetch(
          `https://api.example.com/cities?stateId=${formValues.state}`
        );
        const cities = await response.json();
        return cities.map((c) => ({ value: c.id, label: c.name }));
      },
    },
  ],
};
```

### API Options Features

- **Automatic Loading**: Options are loaded automatically when the field becomes visible
- **Loading State**: Shows "Loading..." indicator while fetching options
- **Auto-clear**: Dependent field values are cleared when dependency changes
- **Error Handling**: Returns empty array on error (you can customize error handling)
- **Access to Form Values**: The options function receives all current form values
- **Caching**: Options are cached per field until dependency changes

### Date Range with Maximum Days

```jsx
{
  name: "startDate",
  type: "date",
  label: "Start Date",
  required: true
},
{
  name: "endDate",
  type: "date",
  label: "End Date",
  required: true,
  // Must be after start date
  dependsOn: {
    field: "startDate",
    condition: "afterDate",
    errorMessage: "End date must be after start date"
  },
  // Can also set maximum date (e.g., 30 days after start)
  // This would need custom validation or calculated max
}
```

## Custom Styling

You can customize the appearance using the `customStyles` prop:

```jsx
const customStyles = {
  container: {
    maxWidth: "800px",
    margin: "0 auto",
  },
  form: {
    backgroundColor: "#f5f5f5",
    borderRadius: "8px",
    padding: "30px",
  },
  title: {
    color: "#333",
    fontSize: "24px",
    textAlign: "center",
  },
  fieldContainer: {
    marginBottom: "20px",
  },
  label: {
    color: "#333",
    fontSize: "14px",
    fontWeight: "600",
  },
  input: {
    border: "2px solid #e0e0e0",
    borderRadius: "6px",
    padding: "10px",
  },
  textarea: {
    border: "2px solid #e0e0e0",
    borderRadius: "6px",
  },
  select: {
    border: "2px solid #e0e0e0",
    borderRadius: "6px",
  },
  button: {
    backgroundColor: "#4caf50",
    color: "white",
    width: "100%",
    padding: "12px",
    fontSize: "16px",
    fontWeight: "600",
  },
  error: {
    color: "#f44336",
    fontSize: "12px",
  },
  // CSS class names (if you prefer CSS classes)
  inputClassName: "custom-input",
  formClassName: "custom-form",
  buttonClassName: "custom-button",
};

<DynamicForm
  formConfig={formConfig}
  onSubmit={handleSubmit}
  customStyles={customStyles}
  className="my-form-container"
/>;
```

## Props

| Prop            | Type     | Required | Default  | Description                        |
| --------------- | -------- | -------- | -------- | ---------------------------------- |
| `formConfig`    | object   | Yes      | -        | Form configuration object          |
| `initialValues` | object   | No       | `{}`     | Initial form values                |
| `onSubmit`      | function | Yes      | -        | Submit handler function            |
| `className`     | string   | No       | `''`     | CSS class for container            |
| `customStyles`  | object   | No       | `{}`     | Custom styles object               |
| `renderField`   | function | No       | `null`   | Custom field renderer              |
| `mode`          | string   | No       | `'edit'` | Form mode: `'edit'` or `'preview'` |
| `onChange`      | function | No       | -        | Callback when form values change   |
| `onFieldBlur`   | function | No       | -        | Callback when field loses focus    |
| `onStepChange`  | function | No       | -        | Callback when step changes         |
| `ref`           | ref      | No       | -        | Ref for form state management      |

### onSubmit Function

```jsx
const handleSubmit = (values, actions) => {
  // values: form values object
  // actions: Formik form actions (setSubmitting, resetForm, etc.)

  // Your submission logic here
  actions.setSubmitting(false);
};
```

### Material-UI (MUI) Support

The package includes a built-in MUI field renderer for Material-UI components. MUI v5+ uses CSS-in-JS (Emotion) for styling, so no separate CSS file is needed.

#### Setup Requirements

1. **Install MUI and its peer dependencies:**
```bash
npm install @mui/material @emotion/react @emotion/styled
```

2. **Wrap your app with ThemeProvider:**
```jsx
import { ThemeProvider, createTheme } from '@mui/material/styles';
import { CssBaseline } from '@mui/material';
import { DynamicForm, muiFieldRenderer } from '@sandesh4ever/react-dynamic-form-builder';

const theme = createTheme({
  // Your theme configuration
  palette: {
    primary: {
      main: '#1976d2',
    },
  },
});

function App() {
  return (
    <ThemeProvider theme={theme}>
      <CssBaseline /> {/* Optional but recommended */}
      <DynamicForm
        formConfig={formConfig}
        onSubmit={handleSubmit}
        renderField={muiFieldRenderer}
      />
    </ThemeProvider>
  );
}
```

#### Using MUI Renderer

```jsx
import { DynamicForm, muiFieldRenderer } from '@sandesh4ever/react-dynamic-form-builder';

const formConfig = {
  title: "User Registration",
  fields: [
    {
      name: "email",
      type: "email",
      label: "Email Address",
      required: true,
    },
    {
      name: "country",
      type: "select",
      label: "Country",
      options: [
        { value: "us", label: "United States" },
        { value: "uk", label: "United Kingdom" },
      ],
    },
  ],
};

function App() {
  return (
    <DynamicForm
      formConfig={formConfig}
      onSubmit={handleSubmit}
      renderField={muiFieldRenderer} // Use MUI components
    />
  );
}
```

**Note:** MUI uses CSS-in-JS via Emotion, so all styles are automatically included. No additional CSS imports are needed. The `muiFieldRenderer` supports:
- Text inputs (text, email, password, tel, url, number)
- Textarea
- Select dropdowns
- Date/Time inputs (date, datetime-local, time, month, week)

### Custom Field Renderer

You can provide a custom renderer for fields:

```jsx
const renderField = (field, formikProps) => {
  // Custom rendering logic
  return <div>{/* Your custom field component */}</div>;
};

<DynamicForm
  formConfig={formConfig}
  onSubmit={handleSubmit}
  renderField={renderField}
/>;
```

## Validation

Validation is automatically handled using Yup based on your field configuration:

- **Required fields**: Automatically validated
- **Email fields**: Email format validation
- **Number fields**: Number type and min/max validation
- **Date fields**: Date format validation
- **Time fields**: Time format validation (HH:MM)
- **Text fields**: minLength/maxLength validation
- **Pattern**: Custom regex pattern validation
- **Field Dependencies**: Fields with dependencies are validated based on dependency conditions
- **Password Confirmation**: Confirm password fields automatically validate against the password field

## License

MIT

## Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

## Support

If you encounter any issues or have questions, please open an issue on GitHub.