JSPM

  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 200
  • Score
    100M100P100Q73482F
  • License MIT

Headless, type-safe form generation engine for React. Schema-driven with full TypeScript support.

Package Exports

  • @classytic/formkit

Readme

@classytic/formkit

A headless, type-safe form generation engine for React 18+ and React 19. Build dynamic forms from schemas with full TypeScript support and zero UI opinions.

npm version License: MIT

โœจ Features

  • ๐ŸŽฏ Headless & UI-Agnostic: Bring your own components (Shadcn, Material UI, Ant Design, or custom)
  • ๐Ÿ“ Schema-Driven: Generate complex forms from simple JSON schemas
  • ๐Ÿ”’ Type-Safe: Built with TypeScript, full type inference and autocomplete
  • โšก Performance: Optimized rendering with React 18/19 concurrent features
  • ๐ŸŽจ Variants: Support multiple component styles (compact, default, custom)
  • ๐Ÿ”€ Conditional Fields: Show/hide fields based on form values
  • ๐Ÿงฉ Composable: Modular architecture with pluggable components
  • ๐Ÿช React Hook Form: Built on top of the industry-standard form library
  • ๐ŸŒฒ Tree-Shakeable: Only bundle what you use

๐Ÿ“ฆ Installation

npm install @classytic/formkit react-hook-form
# or
yarn add @classytic/formkit react-hook-form
# or
pnpm add @classytic/formkit react-hook-form

๐Ÿš€ Quick Start

1. Create a Component Adapter

Map your UI components to form field types:

// form-adapter.tsx
import { FormSystemProvider } from "@classytic/formkit";
import { Input } from "@/components/ui/input";
import { Select } from "@/components/ui/select";
import { Checkbox } from "@/components/ui/checkbox";

const components = {
  text: Input,
  email: Input,
  select: Select,
  checkbox: Checkbox,
  // Add more field types as needed
};

const layouts = {
  grid: ({ children, cols = 1 }) => (
    <div className={`grid grid-cols-${cols} gap-4`}>{children}</div>
  ),
  section: ({ title, description, children }) => (
    <div className="space-y-4">
      {title && <h3 className="text-lg font-semibold">{title}</h3>}
      {description && <p className="text-sm text-muted-foreground">{description}</p>}
      {children}
    </div>
  ),
};

export function FormProvider({ children }: { children: React.ReactNode }) {
  return (
    <FormSystemProvider components={components} layouts={layouts}>
      {children}
    </FormSystemProvider>
  );
}

2. Define Your Schema

import type { FormSchema } from "@classytic/formkit";

const userFormSchema: FormSchema = {
  sections: [
    {
      title: "Personal Information",
      cols: 2,
      fields: [
        {
          name: "firstName",
          type: "text",
          label: "First Name",
          required: true,
        },
        {
          name: "lastName",
          type: "text",
          label: "Last Name",
          required: true,
        },
        {
          name: "email",
          type: "email",
          label: "Email",
          fullWidth: true,
        },
      ],
    },
    {
      title: "Account Settings",
      fields: [
        {
          name: "role",
          type: "select",
          label: "Role",
          options: [
            { value: "user", label: "User" },
            { value: "admin", label: "Admin" },
          ],
        },
        {
          name: "notifications",
          type: "checkbox",
          label: "Enable notifications",
          // Conditional field - only show for admins
          condition: (values) => values.role === "admin",
        },
      ],
    },
  ],
};

3. Render Your Form

import { useForm } from "react-hook-form";
import { FormGenerator } from "@classytic/formkit";
import { FormProvider } from "./form-adapter";

function UserForm() {
  const form = useForm();

  const onSubmit = (data: any) => {
    console.log("Form data:", data);
  };

  return (
    <FormProvider>
      <form onSubmit={form.handleSubmit(onSubmit)}>
        <FormGenerator schema={userFormSchema} control={form.control} />
        <button type="submit">Submit</button>
      </form>
    </FormProvider>
  );
}

๐Ÿ“– API Reference

<FormSystemProvider>

The root provider that registers your components.

<FormSystemProvider components={componentsMap} layouts={layoutsMap}>
  {children}
</FormSystemProvider>

Props:

  • components: Object mapping field types to React components
  • layouts: Object mapping layout types to React components
  • children: React children

<FormGenerator>

Renders forms from schemas.

<FormGenerator schema={schema} control={control} disabled={false} variant="default" />

Props:

  • schema: Form schema object
  • control?: React Hook Form control (optional if inside FormProvider)
  • disabled?: Global disabled state
  • variant?: Component variant to use

Type Exports

import type {
  FormSchema,
  BaseField,
  Section,
  FieldComponentProps,
  ComponentRegistry,
} from "@classytic/formkit";

๐ŸŽจ Advanced Usage

Variants

Support multiple UI styles:

const components = {
  text: DefaultInput,
  compact: {
    text: CompactInput,
    select: CompactSelect,
  },
};

// Use compact variant
<FormGenerator schema={schema} variant="compact" />

Conditional Fields

{
  name: "vatNumber",
  type: "text",
  label: "VAT Number",
  condition: (values) => values.country === "EU",
}

Custom Renderers

{
  title: "Custom Section",
  render: ({ control, disabled }) => (
    <div>Your custom JSX here</div>
  ),
}

Full Width Fields

{
  name: "description",
  type: "textarea",
  label: "Description",
  fullWidth: true, // Spans all columns
}

๐Ÿงช Testing

npm test

๐Ÿ“„ License

MIT ยฉ Classytic

๐Ÿค Contributing

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