npm package: @reactfast/forms
Description: Dynamic React forms with JSON schema, modifiers, and subforms
Author: Jonathon McClendon
Latest version: 0.1.29

# 🎛️ @reactfast/forms

test auto publish

**Dynamic React forms powered by JSON schemas, modifiers, and subforms.**  
Create complex, adaptive form systems without boilerplate — designed for scale, simplicity, and composability.

[![npm version](https://img.shields.io/npm/v/@reactfast/forms.svg)](https://www.npmjs.com/package/@reactfast/forms)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
[![Build](https://github.com/jonathonmcclendon/NovaForms/actions/workflows/build.yml/badge.svg)](https://github.com/jonathonmcclendon/NovaForms/actions)
[![Contributions welcome](https://img.shields.io/badge/contributions-welcome-brightgreen.svg)](https://github.com/jonathonmcclendon/NovaForms/issues)

### 📚 [**View Live Documentation**](https://nova-forms-next.vercel.app/) 📚

---

## ⚙️ Installation

```bash
npm install @reactfast/forms
# or
yarn add @reactfast/forms
```

CtrlForm requires **React 18+** and **React DOM 18+** as peer dependencies.

---

## 🚀 Quick Start

The simplest way to get started:

```jsx
import { useState } from "react";
import { Form, createFormHandler } from "@reactfast/forms";

const fields = [
  { name: "firstName", title: "First Name", type: "string", width: 50 },
  { name: "lastName", title: "Last Name", type: "string", width: 50 },
  { name: "email", title: "Email", type: "email", width: 100 },
  { name: "subscribe", title: "Subscribe?", type: "boolean", width: 100 },
];

export default function App() {
  const [formData, setFormData] = useState({});

  const handleChange = createFormHandler({
    fields,
    setState: setFormData,
  });

  return <Form fields={fields} onChange={handleChange} formData={formData} />;
}
```

---

## ✨ Features

- ⚡ **Controlled forms** — simple `value`/`onChange` pattern like React inputs
- 🧩 **Composable** — each field is a reusable React component
- 🔄 **Advanced conditional logic** — dynamic show/hide, disable, and field dependencies
- 📱 **Responsive layout** — automatic width handling with Tailwind classes
- 🧱 **Subforms & arrays** — nested or repeated field groups are first-class citizens
- 🎨 **Theming-ready** — customize UI with Tailwind or your own design system
- 🔌 **Extensible** — register your own field components via `registerField()`
- 🧠 **Smart rules system** — powerful top-level rules with field-level triggers
- 🔢 **Math operations** — automatic calculations with add, subtract, multiply, divide
- 📝 **String operations** — concatenation and text manipulation
- ✅ **Pattern validation** — client-side regex validation with custom messages
- 🎯 **Multiple field types** — 20+ built-in field types from text to file uploads

---

## 🎯 Built-in Field Types

Nova Forms comes with 20+ field types ready to use:

| Type           | Description                 | Example                                     |
| -------------- | --------------------------- | ------------------------------------------- |
| `string`       | Text input                  | `{ type: "string", title: "Name" }`         |
| `text`         | Textarea                    | `{ type: "text", title: "Description" }`    |
| `email`        | Email input with validation | `{ type: "email", title: "Email" }`         |
| `tel`          | Phone number input          | `{ type: "tel", title: "Phone" }`           |
| `url`          | URL input                   | `{ type: "url", title: "Website" }`         |
| `number`       | Number input                | `{ type: "number", title: "Age" }`          |
| `boolean`      | Checkbox                    | `{ type: "boolean", title: "Subscribe" }`   |
| `toggle`       | Toggle switch               | `{ type: "toggle", title: "Enable" }`       |
| `date`         | Date picker                 | `{ type: "date", title: "Birth Date" }`     |
| `datetime`     | Date and time picker        | `{ type: "datetime", title: "Event Time" }` |
| `time`         | Time picker                 | `{ type: "time", title: "Start Time" }`     |
| `color`        | Color picker                | `{ type: "color", title: "Theme Color" }`   |
| `select`       | Single select dropdown      | `{ type: "select", options: [...] }`        |
| `multiselect`  | Multi-select dropdown       | `{ type: "multiselect", options: [...] }`   |
| `radio`        | Radio button group          | `{ type: "radio", options: [...] }`         |
| `file`         | File upload                 | `{ type: "file", title: "Upload" }`         |
| `fileV2`       | Enhanced file upload        | `{ type: "fileV2", title: "Photo" }`        |
| `uploadToBase` | Base64 image upload         | `{ type: "uploadToBase", title: "Avatar" }` |
| `array`        | Dynamic subform/array       | `{ type: "array", fields: [...] }`          |
| `subForm`      | Nested form group           | `{ type: "subForm", fields: [...] }`        |
| `signature`    | Signature pad               | `{ type: "signature", title: "Signature" }` |
| `rating`       | Star rating                 | `{ type: "rating", title: "Rating" }`       |
| `scale`        | Likert scale                | `{ type: "scale", title: "Satisfaction" }`  |
| `captcha`      | reCAPTCHA                   | `{ type: "captcha" }`                       |
| `header`       | Section header              | `{ type: "header", title: "Section" }`      |
| `paragraph`    | Static text                 | `{ type: "paragraph", content: "Text" }`    |
| `image`        | Static image                | `{ type: "image", image: { src: "..." } }`  |

---

## 🧩 Example: Registering Custom Fields

You can extend Nova Forms with your own field types:

```jsx
import { registerField } from "@reactfast/forms";

function QRCodeScannerField({ field, value, onChange }) {
  return (
    <div>
      <p>Scan QR Code for {field.title}</p>
      {/* Your scanner logic */}
    </div>
  );
}

registerField("qrScanner", QRCodeScannerField);
```

Now use it in your fields array:

```jsx
const fields = [
  {
    name: "eventCheckIn",
    title: "Check In",
    type: "qrScanner",
    width: 100,
  },
];
```

---

## 🧠 API Overview

### `Form`

Renders a form based on your field array with integrated modifiers and conditions.

| Prop           | Type                   | Description                             |
| -------------- | ---------------------- | --------------------------------------- |
| `fields`       | `array`                | Array of field definitions              |
| `onChange`     | `function`             | Change handler (from createFormHandler) |
| `formData`     | `object`               | Form data object from parent state      |
| `theme`        | `object` _(optional)_  | Custom theme overrides                  |
| `isMobileView` | `boolean` _(optional)_ | Force mobile layout (full width)        |

### `createFormHandler`

Creates a change handler that manages state and applies modifiers.

| Prop       | Type                 | Description                                  |
| ---------- | -------------------- | -------------------------------------------- |
| `fields`   | `array`              | Array of field definitions                   |
| `setState` | `function`           | React setState function                      |
| `rules`    | `array` _(optional)_ | Top-level rules referenced by field triggers |

### Field Schema

Each field object supports:

| Property                  | Type                                                      | Description                                       |
| ------------------------- | --------------------------------------------------------- | ------------------------------------------------- |
| `name`                    | `string`                                                  | Field name (required)                             |
| `type`                    | `string`                                                  | Field type (string, email, boolean, etc.)         |
| `title`                   | `string`                                                  | Display label (preferred over `label`)            |
| `label`                   | `string`                                                  | Display label (legacy, use `title`)               |
| `width`                   | `number`                                                  | Width percentage (25, 50, 75, 100)                |
| `default`                 | `any`                                                     | Default value                                     |
| `readOnly`                | `boolean`                                                 | Make field read-only                              |
| `required`                | `boolean`                                                 | Mark field as required                            |
| `placeholder`             | `string`                                                  | Placeholder text                                  |
| `description`             | `string`                                                  | Help text below field                             |
| `helper`                  | `string`                                                  | Additional help text                              |
| `error`                   | `string`                                                  | Error message to display                          |
| `leadingIcon`             | `Component`                                               | Icon component before input                       |
| `trailingIcon`            | `Component`                                               | Icon component after input                        |
| `modifiers`               | `array`                                                   | (Legacy) field-local modifiers for values         |
| `triggers`                | `array`                                                   | Triggers that reference top-level rules           |
| `conditions.hiddenWhen`   | `array or object`                                         | Conditions to hide (rendered with `hidden` class) |
| `conditions.hiddenMode`   | `any or all`                                              | Mode for hidden conditions (default any)          |
| `conditions.readOnlyWhen` | `array or object`                                         | Conditions to set readOnly                        |
| `conditions.readOnlyMode` | `any or all`                                              | Mode for readOnly conditions (default any)        |
| `pattern`                 | `RegExp \| string \| Array<{ regex, message } \| string>` | Client-side pattern checks with messages          |
| `options`                 | `array`                                                   | Options for select, radio, multiselect fields     |
| `fields`                  | `array`                                                   | Sub-fields for array/subForm types                |

### Modifiers (legacy)

Field-local modifiers automatically update dependent field values. These are still supported for backward compatibility, but the preferred approach is to use top-level rules and field-level triggers.

```jsx
{
  name: "firstName",
  type: "string",
  modifiers: [
    {
      target: "fullName",
      type: "concat",
      when: "true",
      value: " " // adds space
    }
  ]
}
```

### Conditions

Control field visibility and state:

```jsx
{
  name: "subscribe",
  type: "boolean",
  conditions: {
    hiddenWhen: {
      field: "age",
      when: "less than",
      value: 18
    }
  }
}
```

---

### Rules & Triggers

- Rules live at the top level and have unique names. A rule contains one or more effects that target a field and either change its value or attributes.
- Triggers live on fields and reference a rule by name. When the trigger's conditions match, the rule's effects are applied.
- Value effects are applied inside `createFormHandler`. Attribute effects (e.g., `hidden`, `readOnly`, `title`) are applied in `NovaForm`.

Rules shape:

```js
const rules = [
  {
    name: "fullNameRule",
    effects: [
      {
        targetField: "displayName",
        prop: "value",
        type: "concat",
        kind: "string",
        value: " ",
      },
      { targetField: "age", prop: "readOnly", value: true },
    ],
  },
];
```

Trigger shape on a field:

```js
{
  name: "firstName",
  type: "string",
  triggers: [
    {
      rule: "fullNameRule",
      when: [
        { field: "firstName", when: "not empty" },
        { field: "lastName", when: "not empty" },
      ],
      mode: "all", // all = AND, any = OR (default)
    },
  ],
}
```

Pass `rules` to both `createFormHandler` and `Form`:

```jsx
const handleChange = createFormHandler({
  fields,
  rules,
  setState: setFormData,
});

<Form
  fields={fields}
  rules={rules}
  onChange={handleChange}
  formData={formData}
/>;
```

Hidden fields remain mounted and use Tailwind's `hidden` class so values still update.

---

## 🧱 Architecture Overview

Nova Forms is organized for **extensibility** and **maintainability**:

```
src/
├── core/              → field registry and evaluation
├── formFields/        → built-in field components
├── handlers/          → form handlers and modifiers
├── utils/             → shared utilities
├── NovaForm.jsx       → main form component
└── returnFields.jsx   → field renderer
```

---

## 🔄 Migration from Manual Field Mapping

If you're currently mapping fields manually:

**Before:**

```jsx
import {
  ReturnFieldsV2,
  createFormHandler,
  initializeFormData,
} from "@reactfast/forms";

const [formData, setFormData] = useState(() => initializeFormData(fields));
const handleChange = createFormHandler({ fields, setState: setFormData });

return (
  <div className="-mx-2 flex flex-wrap">
    {fields.map((field) => (
      <div
        key={field.name}
        className={`${getWidthClass(field.width)} mb-4 px-2`}
      >
        <ReturnFieldsV2
          field={field}
          value={formData[field.name]}
          onChange={handleChange}
        />
      </div>
    ))}
  </div>
);
```

**After:**

```jsx
import { Form, createFormHandler } from "@reactfast/forms";

const [formData, setFormData] = useState({});
const handleChange = createFormHandler({ fields, setState: setFormData });

return <Form fields={fields} onChange={handleChange} formData={formData} />;
```

---

## 📚 Documentation

For comprehensive guides and examples, see our documentation:

- **Live Documentation**: [nova-forms-next.vercel.app](https://nova-forms-next.vercel.app/)
- **Docs Repository**: [jonathonmcclen/NovaForms-Next](https://github.com/jonathonmcclen/NovaForms-Next)

- **[Introduction](documentation/intro.md)** - Complete overview of Nova Forms
- **[Quick Start](documentation/quickstart.md)** - Get up and running quickly
- **[createFormHandler](documentation/createFormHandler.md)** - Understanding the form handler system
- **[Fields & Schemas](documentation/fields-schemas.md)** - Complete field reference and schema guide
- **[Rules System](documentation/rules.md)** - Advanced rules and effects
- **[Triggers & Conditions](documentation/triggers.md)** - Conditional logic and triggers
- **[Dynamic Hide](documentation/dynamic-hide.md)** - Show/hide fields dynamically
- **[Dynamic Disable](documentation/dynamic-disable.md)** - Enable/disable fields dynamically
- **[Custom Fields](documentation/custom-fields.md)** - Creating and registering custom field types
- **[Styling with Tailwind](documentation/styling-tailwind.md)** - Tailwind CSS integration
- **[Theme Styling](documentation/styling-theme.md)** - Custom theming system

---

## 🤝 Contributing

We welcome pull requests and feature suggestions!

1. Fork the repo
2. Create a feature branch

   ```bash
   git checkout -b feature/your-feature
   ```

3. Commit your changes

   ```bash
   git commit -m "Add new feature"
   ```

4. Push to your branch

   ```bash
   git push origin feature/your-feature
   ```

5. Open a pull request

> 🔒 Only approved code owners can merge to main.
> See `.github/CODEOWNERS` for details.

---

## 🪪 License

Licensed under the [MIT License](LICENSE).
Copyright © 2025 [Jonathon McClendon](https://github.com/jonathonmcclendon)

---

## 💡 Maintained by

**Jonathon McClendon**
Creator of Nova Forms — building high-performance tools for scalable React ecosystems.

---

### 🌟 Support the Project

If Nova Forms helps you ship faster or cleaner React code:

- ⭐ Star the repo
- 🐛 Open an issue for bugs or feature ideas
- 💬 Share it with other developers

---

> _"A form library that feels invisible — flexible, composable, and future-proof."_

---

### 🧭 Next Steps (Roadmap Ideas)

- [ ] Advanced validation layer (Yup / Zod integration)
- [ ] Enhanced theming system (context-aware)
- [ ] Multi-Step Forms / Wizard support
- [ ] TypeScript definitions (optional)

---