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

@juspay/deformed

v0.2.4

Published

Deformed — a schema-driven form engine with React bindings

Downloads

994

Readme


Features

  • Schema-first — Define entire forms as JSON: fields, validation, conditional logic, API calls, and output transformations
  • Headless core@juspay/deformed-core is UI-agnostic; plug in any rendering layer
  • React integration@juspay/deformed-react provides hooks, context providers, and a full renderer built on React Hook Form + shadcn/ui
  • 30+ field types — text, number, select, combobox, date, datepicker, switch, checkbox, radiogroup, slider, toggle, togglegroup, codemirror, sqlquery, button, tabs, accordion, wizard, array, stack, object, permutation, tagsinput, formattedlistinput, plus a family of read-only preview* displays (text, tag, taglist, table, card, infobox, json)
  • Validation engine — Zod-powered with 10+ rules, dynamic required via logic rules, and automatic skip-when-hidden
  • Logic engine — 12 comparison operators + AND/OR/NOT combinators for conditional visibility, disabling, and dependencies
  • Action system — 10 action types with conditional execution, debounce, and recursive chaining
  • Template engine{{field}} interpolation with dot-path access and pipe fallbacks in URLs, request bodies, and display text
  • Output transformation — Field mapping, request body templating, and custom transforms for API submission
  • Dynamic options — Static, API-fetched, and cascading dropdown options with dataKey extraction and dataTransformer
  • Custom component registry — Register your own components and use them in schemas

Packages

| Package | Description | | ------------------------------------------ | ------------------------------------------------------------------------------------- | | @juspay/deformed-core | Schema types, validation engine (Zod), logic evaluator, template engine, output utils | | @juspay/deformed-react | React hooks, context providers, field components (shadcn/ui), form renderer | | docs | Documentation site (Docusaurus) |


Installation

The umbrella package bundles core + react in a single install:

npm install @juspay/deformed
# or: yarn add @juspay/deformed
# or: pnpm add @juspay/deformed

You can also install directly from the GitHub repo (useful for testing unreleased commits — the prepare script builds on install):

npm install github:juspay/deformed
# or pin a branch / tag / commit:
npm install github:juspay/deformed#main
npm install github:juspay/deformed#v0.1.14

If you prefer the individual packages, those are still published too:

npm install @juspay/deformed-core @juspay/deformed-react

Peer dependencies: react >= 19, react-dom >= 19 · Node >= 22


Agent Skill

For AI coding assistants (Claude Code, Cursor, etc.), this repo also ships a bundled agent skill that teaches the assistant how to author and edit Deformed schemas. It follows the agentskills.io format and installs via the vercel-labs/skills CLI:

# Direct path to the skill in this repo
npx skills add https://github.com/juspay/deformed/tree/main/skills/deformed

Once installed, the assistant gains:

  • The mental model and authoring rules from SKILL.md.
  • A full type reference in references/REFERENCE.md and a 13-recipe cookbook in references/EXAMPLES.md, both loaded on demand via the skill's progressive-disclosure model.
  • A Node CLI at scripts/validate.mjs that validates a FormSchema JSON against the published Zod definition. The script imports validateSchema from @juspay/deformed-core, so it reports identically to the MCP server's deformed_validate_schema tool. Host environments can bind it to a slash command (e.g. /validate path/to/schema.json).

For environments that prefer MCP, the same validation surface is also available via the @juspay/deformed-mcp-server package (deformed_validate_schema, deformed_generate_schema, deformed_edit_schema, deformed_get_field_reference).


Quick Start

1. Define a schema

import { FormSchema } from '@juspay/deformed-core';

const schema: FormSchema = {
  title: 'Contact Form',
  fields: [
    {
      type: 'text',
      name: 'name',
      label: 'Full Name',
      validation: { required: 'Name is required' },
    },
    {
      type: 'text',
      name: 'email',
      label: 'Email',
      inputType: 'email',
      validation: { required: true, email: 'Enter a valid email' },
    },
    {
      type: 'select',
      name: 'subject',
      label: 'Subject',
      options: [
        { label: 'General', value: 'general' },
        { label: 'Support', value: 'support' },
        { label: 'Sales', value: 'sales' },
      ],
    },
    {
      type: 'text',
      name: 'message',
      label: 'Message',
      multiline: true,
      rows: 4,
      validation: { required: true, minLength: { value: 10, message: 'At least 10 characters' } },
    },
  ],
};

2. Render with React

import { DeformedProvider, DeformedRenderer, useDeformed } from '@juspay/deformed-react';

function ContactForm() {
  const methods = useDeformed(schema);

  return (
    <DeformedProvider>
      <DeformedRenderer schema={schema} methods={methods} />
    </DeformedProvider>
  );
}

DeformedProvider wires the registry, options, loading, and error contexts. DeformedRenderer takes the parsed schema and the React Hook Form methods returned by useDeformed.

3. Or use the hook for full control

import { useDeformed } from '@juspay/deformed-react';

function CustomForm() {
  const methods = useDeformed(schema);

  return (
    <form onSubmit={methods.handleSubmit((data) => console.log(data))}>
      {/* Build your own UI using methods.control, methods.register, etc. */}
    </form>
  );
}

Schema Overview

A form schema is a JSON object that describes the entire form:

{
  title?: string;
  description?: string;
  fields: FormNode[];         // Fields and containers
  actions?: FormButtonSchema[]; // Form-level buttons
  api?: {
    submitUrl: string;
    method: 'POST' | 'PUT' | 'PATCH';
    headers?: Record<string, string>;
    bodyTemplate?: Record<string, any>;     // Supports {{field}} interpolation
    outputMapping?: OutputFieldMapping[];   // Source → target field mapping
  };
}

Field Types

Input Fields

| Type | Key Props | | -------------------- | -------------------------------------------------------------- | | text | inputType (text/email/password/url/tel), multiline, rows | | number | min, max, step | | select | options (static or API), multi, searchable | | combobox | options, multi, searchable | | date | minDate, maxDate, format | | datepicker | minDate, maxDate, format | | switch | labelPosition | | checkbox | labelPosition | | radiogroup | options, orientation | | slider | min, max, step | | toggle | variant, size | | togglegroup | options, selectionMode, orientation, variant, size | | codemirror | language, theme | | sqlquery | SQL editor with parsed-array output | | button | variant, size | | tabs | tabs[] (label, fields, hidden), defaultActiveIndex | | accordion | items[] (title, fields, hidden), multiple, collapsible | | wizard | steps[] (title, description, fields), orientation | | array | fields[], itemLabel | | stack | repeated row template with shared add/remove | | object | nested-object grouping with shared-value sync | | permutation | cartesian-product field combinator | | tagsinput | free-form tag input | | formattedlistinput | textarea split into a structured list |

Read-only / Preview Fields

previewtext, previewtag, previewtaglist, previewtable, previewcard, previewinfobox, previewjson — bind to a sourceField and render its value in different display modes.

Layout

| Type | Description | | ----------- | ---------------------------------------------------------- | | container | Groups fields with optional className and hidden logic |

Fields and containers can be nested to create any layout structure.


Validation

Validation rules are declared per-field in the schema and compiled to Zod schemas at runtime.

{
  type: 'text',
  name: 'username',
  validation: {
    required: 'Username is required',
    minLength: { value: 3, message: 'At least 3 characters' },
    maxLength: 50,
    pattern: { value: '^[a-zA-Z0-9_]+$', message: 'Alphanumeric and underscores only' },
  },
}

Available Rules

| Rule | Type | Description | | ----------- | ------------------------------ | --------------------------------------- | | required | boolean \| string | Required field, optional custom message | | min | number \| {value, message} | Minimum numeric value | | max | number \| {value, message} | Maximum numeric value | | minLength | number \| {value, message} | Minimum string length | | maxLength | number \| {value, message} | Maximum string length | | pattern | string \| {value, message} | Regex validation | | email | boolean \| string | Email format | | url | boolean \| string | URL format | | uuid | boolean \| string | UUID format | | custom | (value) => boolean \| string | Custom validator function |

Hidden fields are automatically skipped during validation.


Logic Engine

Control field visibility and state with declarative logic rules:

{
  type: 'text',
  name: 'companyName',
  label: 'Company Name',
  // Only visible when userType equals "business"
  hidden: {
    not: { var: 'userType', op: 'equals', value: 'business' }
  },
}

Operators

equals · notEquals · greaterThan · greaterThanOrEqual · lessThan · lessThanOrEqual · contains · notContains · in · notIn · isEmpty · isNotEmpty

Combinators

{
  and: [rule1, rule2];
}
{
  or: [rule1, rule2];
}
{
  not: rule;
}

Logic rules can also be expressed as string DSL: "field == value", "field != value", etc.


Action System

Define actions that execute on field events:

{
  type: 'select',
  name: 'country',
  options: [...],
  events: {
    onChange: [
      {
        type: 'api_call',
        config: {
          url: '/api/states?country={{country}}',
          method: 'GET',
          onSuccess: [
            { type: 'set_options', target: 'state', dataKey: 'data' },
            { type: 'clear_value', target: ['state', 'city'] },
          ],
        },
      },
    ],
  },
}

Action Types

| Type | Description | | ------------- | ------------------------------------------------------------------------------- | | set_value | Set a field's value | | clear_value | Clear one or more fields | | set_options | Update a select field's options | | transform | Evaluate a template expression against form state and write it to a field | | api_call | Make an API request with onSuccess/onError chaining | | fetch_data | Re-fire a host's deferred fetch by name (paired with manual select/container) | | custom | Invoke a custom-registered action by name | | submit | Submit the form | | reset | Reset the form | | navigate | Navigate to a URL |

All actions support condition (conditional execution) and debounce.


API Integration

Dynamic Options from API

{
  type: 'select',
  name: 'category',
  options: {
    api: '/api/categories',
    method: 'GET',
    dataKey: 'data.categories',     // Dot-path to array in response
    labelKey: 'name',
    valueKey: 'id',
    dataTransformer: (item) => ({ label: item.title, value: item.code }),
  },
}

Form Submission

{
  fields: [...],
  api: {
    submitUrl: '/api/forms/submit',
    method: 'POST',
    headers: { 'X-Custom': 'value' },
    bodyTemplate: {
      user: { name: '{{name}}', email: '{{email}}' },
      metadata: { submitted_at: '{{_now}}' },
    },
    outputMapping: [
      { source: 'fullName', target: 'user.name' },
      { source: 'age', transform: (v) => parseInt(v) },
    ],
  },
}

Architecture

deformed/
├── packages/
│   ├── core/                    # @juspay/deformed-core
│   │   └── src/
│   │       ├── schema/          # Type definitions (field, form, action, logic, container)
│   │       ├── validation/      # Zod-based validation engine
│   │       ├── logic/           # Logic rule evaluator + string DSL parser
│   │       └── utils/           # Template interpolation, output transforms
│   └── react/                   # @juspay/deformed-react
│       └── src/
│           ├── hooks/           # useDeformed, useLogic, useTemplate, useActionExecutor
│           ├── context/         # DeformedContext, OptionsContext, LoadingContext
│           ├── components/
│           │   ├── ui/          # shadcn/ui base components
│           │   ├── fields/      # Field type components
│           │   └── renderer/    # FormNodeRenderer, DeformedRenderer
│           └── utils/           # Submit handler
└── apps/
    └── docs/                    # Docusaurus documentation site

Core Design Principles

  1. Schema as truth — The JSON schema is the single source of truth for the form
  2. Separation of concerns — Core engine knows nothing about React or DOM
  3. Composable — Use the full renderer, individual hooks, or mix and match
  4. Extensible — Custom components via registry, custom actions, custom validators

Development

This is a Turborepo monorepo.

Prerequisites

  • Node.js >= 18
  • npm >= 10 (or bun)

Setup

git clone <repo-url>
cd deformed
npm install

Commands

| Command | Description | | ---------------------- | ------------------------------------ | | npm run dev | Start all packages in dev/watch mode | | npm run build | Build all packages | | npm run lint | Lint all packages | | npm run lint:fix | Auto-fix lint issues | | npm run format | Format all files with Prettier | | npm run format:check | Check formatting | | npm run clean | Clean all build outputs |

Package-specific

# Build just core
cd packages/core && npm run build

# Build just react
cd packages/react && npm run build

# Run docs locally
cd apps/docs && npm start

Tech Stack

  • TypeScript — Full type safety across core and react packages
  • React Hook Form — Form state management
  • Zod — Runtime validation schema generation
  • shadcn/ui + Radix — Accessible UI components
  • dnd-kit — Drag-to-reorder rows in ArrayField (opt-in via sortable: true); primitives re-exported for custom DnD UIs
  • Turborepo — Monorepo build orchestration
  • Vite — React package bundling
  • Docusaurus — Documentation site

License

Private — Internal use only.