npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@reactfast/forms

v0.1.11

Published

Dynamic React forms with JSON schema, modifiers, and subforms

Readme

🎛️ @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 License: MIT Build Contributions welcome

📚 View Live Documentation 📚


⚙️ Installation

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:

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:

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:

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.

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

Conditions

Control field visibility and state:

{
  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:

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:

{
  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:

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:

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:

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:


🤝 Contributing

We welcome pull requests and feature suggestions!

  1. Fork the repo

  2. Create a feature branch

    git checkout -b feature/your-feature
  3. Commit your changes

    git commit -m "Add new feature"
  4. Push to your branch

    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. Copyright © 2025 Jonathon McClendon


💡 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)