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

react-dynamic-smartform

v2.0.4

Published

Lightweight schema-driven dynamic form builder for React

Readme

⚡ react-dynamic-smartform

✨ Features

| | Feature | Description | |---|---|---| | 📦 | Zero Dependencies | Under 10KB gzipped | | 🧠 | Schema Driven | Entire form UI & logic from a single JSON array | | ✅ | Smart Validation | Required, Email, Min/Max, Regex, Custom functions | | 🔁 | Conditional Logic | showIf / disabled — show, hide, or disable fields dynamically | | 🌐 | API Integration | Fetch dropdown options from remote APIs with built-in caching | | 🧩 | New Field Types | Date range, File upload, Signature pad, Rating, Slider, OTP, Color picker, Repeatable groups | | 🧙 | Multi-step Wizard | SmartFormWizard with progress bar and per-step validation | | 🔧 | Visual Form Builder | Drag-and-drop FormBuilder with live preview and JSON export | | 🪝 | useFormState Hook | Access dirty, isValid, completionPct outside the form | | 🔍 | Dev-time Validation | validateSchema() catches errors before runtime | | 📖 | Storybook Generator | Auto-generate .stories.tsx files from any schema | | ⚡ | Optimized Performance | Debounced onChange, smart re-rendering | | 🧩 | Full TypeScript Support | Complete type definitions for every prop and schema key |


📦 Installation

# npm
npm install react-dynamic-smartform
 
# yarn
yarn add react-dynamic-smartform
 
# pnpm
pnpm add react-dynamic-smartform

🚀 Quick Start

1️⃣ Define a Schema

import type { FieldSchema } from "react-dynamic-smartform";
 
const schema: FieldSchema[] = [
  {
    name: "username",
    label: "Username",
    type: "text",
    col: 6,
    validation: { required: "Username is required", minLength: 3 },
    placeholder: "johndoe",
    helpText: "3–20 characters",
  },
  {
    name: "email",
    label: "Email Address",
    type: "email",
    col: 6,
    validation: { required: true, email: true },
  },
  {
    name: "role",
    label: "Role",
    type: "select",
    col: 6,
    options: ["Developer", "Designer", "Manager"],
    validation: { required: true },
  },
  {
    name: "bio",
    label: "Bio",
    type: "textarea",
    col: 12,
    rows: 3,
  },
];

2️⃣ Render the Form

import { SmartForm } from "react-dynamic-smartform";
import "react-dynamic-smartform/styles.css";
 
function App() {
  return (
    <SmartForm
      schema={schema}
      onSubmit={(data) => console.log(data)}
      submitLabel="Create Account"
    />
  );
}

🧩 Field Types

| Type | Description | |---|---| | text | Single-line text input | | email | Email input with format validation | | password | Password input | | number | Numeric input with min/max | | textarea | Multi-line text | | select | Dropdown — static options or remote API | | radio | Radio button group | | checkbox | Single checkbox | | date | Native date picker | | daterange | Start + end date as one field | | file | Drag-and-drop upload with preview, size/type validation | | signature | Canvas-based drawn signature | | rating | Star rating (configurable count) | | slider | Range slider with min/max/step | | colorpicker | Hex/RGBA color picker with presets | | otp | Segmented PIN/OTP input boxes | | repeatable | Add/remove rows of nested field groups |


🧠 SmartForm Props

| Prop | Type | Default | Description | |---|---|---|---| | schema | FieldSchema[] | — | Form field definitions | | onSubmit | (data: Record<string, any>) => void | — | Called on valid submission | | onChange | (data: Record<string, any>) => void | — | Called on any field change | | defaultValues | Record<string, any> | — | Pre-fill field values | | submitLabel | string | "Submit" | Submit button text | | className | string | — | CSS class on the <form> element | | gridCols | number | 12 | Grid columns (used with col on each field) |


📐 FieldSchema Reference

interface FieldSchema {
  // Required
  name: string;          // Unique field key
  label: string;         // Display label
  type: FieldType;       // Field type (see table above)
 
  // Layout
  col?: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12;
 
  // Content
  placeholder?: string;
  helpText?: string;
  defaultValue?: any;
  rows?: number;         // textarea only
 
  // Validation
  validation?: {
    required?: boolean | string;
    email?: boolean | string;
    min?: number | string;
    max?: number | string;
    minLength?: number | string;
    maxLength?: number | string;
    pattern?: { value: RegExp; message: string };
    validate?: (value: any, allValues: Record<string, any>) => true | string;
  };
 
  // Conditional logic
  showIf?: (values: Record<string, any>) => boolean;
  disabled?: boolean | ((values: Record<string, any>) => boolean);
 
  // Data
  options?: string[] | { label: string; value: string }[];
  optionsUrl?: string;   // Fetch options from a remote API
 
  // Field-specific
  stars?: number;        // rating — number of stars (default 5)
  min?: number;          // slider
  max?: number;          // slider
  step?: number;         // slider
  showValue?: boolean;   // slider — show current value badge
  multiple?: boolean;    // file — allow multiple files
  accept?: string;       // file — HTML accept string e.g. "image/*,.pdf"
  maxSize?: number;      // file — max bytes
  otpLength?: number;    // otp — number of digit boxes (default 6)
  startLabel?: string;   // daterange
  endLabel?: string;     // daterange
  fields?: SubFieldSchema[];  // repeatable — sub-field definitions
  addLabel?: string;     // repeatable — "Add" button label
  removeLabel?: string;  // repeatable — "Remove" button label
 
  // Value transform (applied before storing)
  transform?: (value: any) => any;
}

🔁 Conditional Logic

Fields can appear, disappear, or become disabled based on the values of other fields.

const schema: FieldSchema[] = [
  { name: "hasCompany", label: "Signing up as a company?", type: "checkbox" },
 
  // Only visible when hasCompany is checked
  {
    name: "companyName",
    label: "Company Name",
    type: "text",
    showIf: (values) => !!values.hasCompany,
    validation: { required: "Company name is required" },
  },
 
  // Disabled until a country is selected
  {
    name: "state",
    label: "State",
    type: "text",
    disabled: (values) => !values.country,
  },
];

🌐 API-Driven Options

Fetch dropdown or radio options from a remote endpoint. Responses are cached automatically.

{
  name: "country",
  label: "Country",
  type: "select",
  optionsUrl: "https://restcountries.com/v3.1/all?fields=name",
}

The hook normalises common API shapes automatically:

  • { label, value } — used as-is
  • { name, id } — mapped to { label: name, value: id }
  • "string" — used as both label and value

🧙 Multi-step Wizard

import { SmartFormWizard, StepSchema } from "react-dynamic-smartform";
 
const steps: StepSchema[] = [
  {
    title: "Personal Info",
    description: "Tell us about yourself",
    fields: [
      { name: "firstName", label: "First Name", type: "text", col: 6, validation: { required: true } },
      { name: "lastName",  label: "Last Name",  type: "text", col: 6, validation: { required: true } },
    ],
  },
  {
    title: "Contact",
    fields: [
      { name: "email", label: "Email", type: "email", validation: { required: true, email: true } },
      { name: "phone", label: "Phone", type: "text" },
    ],
  },
  {
    title: "Confirm",
    fields: [
      { name: "terms", label: "I agree to the Terms of Service", type: "checkbox", validation: { required: true } },
    ],
  },
];
 
<SmartFormWizard
  steps={steps}
  onSubmit={(data) => console.log(data)}
  submitLabel="Complete Registration"
/>

Each step validates independently before advancing. A progress bar and step indicator are included automatically.


🔧 Visual Form Builder

Drop FormBuilder anywhere to give your users a drag-and-drop schema editor with live preview and JSON export.

import { FormBuilder } from "react-dynamic-smartform";
 
<FormBuilder
  initialSchema={[]}
  onChange={(schema) => console.log("Updated schema:", schema)}
/>

The builder includes a field palette, property editor panel, live form preview, and a raw JSON tab.


🪝 useFormState Hook

Access form state outside the <SmartForm> component — useful for save indicators, progress bars, and enabling/disabling external buttons.

import { useFormState } from "react-dynamic-smartform";
 
function MyPage() {
  const { values, isDirty, isValid, completionPct, errors } = useFormState(schema);
 
  return (
    <>
      <p>Completion: {completionPct}%</p>
      <p>Has unsaved changes: {isDirty ? "Yes" : "No"}</p>
      <p>All valid: {isValid ? "Yes" : "No"}</p>
    </>
  );
}

| Property | Type | Description | |---|---|---| | values | Record<string, any> | Current field values | | errors | Record<string, string> | Current validation errors | | touched | Record<string, boolean> | Which fields have been interacted with | | dirty | Record<string, boolean> | Which fields differ from their default value | | isDirty | boolean | true if any field has changed | | isValid | boolean | true if all visible fields pass validation | | isSubmitting | boolean | true while the onSubmit promise is pending | | completionPct | number | Percentage of required fields that are filled (0–100) |


🔍 Dev-time Schema Validation

Catch schema mistakes early in development with validateSchema().

import { validateSchema } from "react-dynamic-smartform";
 
const warnings = validateSchema(schema);
// [
//   { field: "fields[1] (email)", message: "Duplicate field name "email"", severity: "error" },
//   { field: "fields[2] (picker)", message: "type "select" should have options or optionsUrl", severity: "warning" },
// ]

Or use the hook version — it logs automatically to the browser console in development:

import { useSchemaValidation } from "react-dynamic-smartform";
 
// Call inside your component. Logs grouped warnings in dev, no-op in production.
useSchemaValidation(schema);

Checks include: missing name/label/type, unknown field types, duplicate names, invalid col values, missing options on select/radio fields, and unknown keys.


📖 Storybook Story Generator

Auto-generate Storybook CSF stories from any schema.

import { generateStoriesFile } from "react-dynamic-smartform";
 
const { filename, content } = generateStoriesFile(schema, "ContactForm");
// filename → "ContactForm.stories.tsx"
// content  → full CSF story with Default, Prefilled, and Disabled variants

Use this in a build script or CLI tool to keep stories in sync with your schemas automatically.


🎨 Styling & Theming

Import the default stylesheet:

import "react-dynamic-smartform/dist/smartform.css";

All classes use a sf- prefix and are easy to override with plain CSS. Key classes:

| Class | Element | |---|---| | .sf-form | <form> wrapper | | .sf-grid | Field grid | | .sf-label | Field label | | .sf-input | All input/select/textarea elements | | .sf-input--error | Input in error state | | .sf-error | Error message text | | .sf-help | Help text | | .sf-btn--primary | Submit button | | .sf-btn--ghost | Reset button |

Custom theme example:

.sf-input:focus {
  border-color: #your-brand-color;
  box-shadow: 0 0 0 3px rgba(your-r, your-g, your-b, 0.15);
}
 
.sf-btn--primary {
  background: #your-brand-color;
  border-color: #your-brand-color;
}

🗂️ Full API Reference

// Components
import { SmartForm, SmartFormWizard, FormBuilder } from "react-dynamic-smartform";
 
// Hooks
import { useSmartForm, useFormState } from "react-dynamic-smartform";
 
// Utilities
import { validateField, validateForm, validateSchema, useSchemaValidation } from "react-dynamic-smartform";
import { generateStories, generateStoriesFile } from "react-dynamic-smartform";
 
// Individual field components (for custom renderers)
import {
  TextField, NumberField, SelectField, TextareaField,
  RadioField, CheckboxField, DateRangeField, FileUploadField,
  SignaturePad, RatingField, SliderField, ColorPickerField,
  OTPField, RepeatableField,
} from "react-dynamic-smartform";
 
// Types
import type {
  FieldSchema, SubFieldSchema, StepSchema, FieldType,
  FieldProps, SelectOption, ValidationRule, SmartFormProps,
  ColSpan, FormStateSnapshot, SchemaWarning,
} from "react-dynamic-smartform";

📋 Examples

🤝 Contributing

Contributions, issues, and feature requests are welcome.

git clone https://github.com/Rishitsha/react-dynamic-smartform
cd react-dynamic-smartform
npm install
npm run dev

📄 License

MIT © Rishitsha