npm.io
0.1.2 • Published 6d ago

@hnpsaga/makeform

Licence
MIT
Version
0.1.2
Deps
0
Size
205 kB
Vulns
0
Weekly
348

MakeForm

Schema-driven TypeScript forms for React.

Build forms with strong type inference, validation, state management, dynamic rendering, and extensible UI customization — all from a single schema.

Most form libraries force you to choose between strong typing, dynamic form generation, and flexible UI customization. MakeForm provides all three. Define your form once and get:

  • Strong TypeScript inference
  • Built-in validation
  • Form state management
  • Dynamic form rendering
  • React integration
  • Renderer overrides
  • Custom field support
  • Styling overrides
  • Framework-agnostic core engine

Live Demo

Try MakeForm in action:

https://makeform-demo.vercel.app/


Features

Strong Type Inference
const schema = {
  name: textField(),
  age: numberField(),
  subscribed: checkboxField(),
};

type FormValues = InferValues<typeof schema>;

/*
{
  name: string;
  age: number;
  subscribed: boolean;
}
*/
Schema-Driven Forms

Define your form structure in one place.

const schema = {
  name: textField({
    label: 'Full Name',
    validators: [required('Name is required')],
  }),

  email: emailField({
    label: 'Email Address',
    validators: [required('Email is required'), email()],
  }),

  age: numberField({
    label: 'Age',
    validators: [min(18, 'Must be 18 or older')],
  }),
};
Dynamic Form Rendering

Render complete forms directly from a schema.

<FormRenderer form={form} schema={schema} />
Extensible UI

Replace any built-in field renderer.

<FormRenderer
  form={form}
  schema={schema}
  renderers={{
    text: CustomTextRenderer,
  }}
/>

Or create entirely new field experiences.

<FormRenderer
  form={form}
  schema={schema}
  renderers={{
    custom: {
      richText: RichTextRenderer,
    },
  }}
/>
Styling Overrides

Keep the default theme or integrate with your own design system.

<FormRenderer
  form={form}
  schema={schema}
  classNames={{
    input: 'my-input',
    field: 'my-field',
    error: 'my-error',
  }}
/>

Installation

npm install @hnpsaga/makeform

Peer dependency:

npm install react

Supports:

  • React 18
  • React 19

Quick Start

import { useForm, FormRenderer, textField, emailField, required, email } from '@hnpsaga/makeform';

const schema = {
  name: textField({
    label: 'Name',
    validators: [required()],
  }),

  email: emailField({
    label: 'Email',
    validators: [required(), email()],
  }),
};

export default function App() {
  const form = useForm(schema);

  const submit = form.handleSubmit((values) => {
    console.log(values);
  });

  return (
    <>
      <FormRenderer form={form} schema={schema} />

      <button onClick={submit}>Submit</button>
    </>
  );
}

Core Concepts

1. Schema Definition

Schemas define field types, labels, default values, validation rules, and rendering metadata.

const schema = {
  firstName: textField({
    label: 'First Name',
    validators: [required('First name is required')],
  }),

  age: numberField({
    label: 'Age',
    validators: [min(18, 'Minimum age is 18')],
  }),
};
2. Type Inference

Generate form value types automatically — no manual interfaces required.

type FormValues = InferValues<typeof schema>;
3. Validation

Attach validators directly to fields.

const schema = {
  password: passwordField({
    validators: [required(), min(8)],
  }),
};

Each validator accepts an optional custom error message.

required('Password is required');
min(8, 'Password must be at least 8 characters');
max(50, 'Password must be at most 50 characters');
pattern(/^[a-zA-Z]+$/, 'Letters only');
email('Invalid email address');
phone('Invalid phone number');

When no message is provided, a default message is used.

required()  //"Field is required"
min(3)      //"Minimum length is 3"
max(10)     //"Maximum length is 10"

Validate manually:

const result = form.validate();

if (!result.valid) {
  console.log(result.errors);
}

Built-in validators:

required(message?);
min(limit, message?);
max(limit, message?);
pattern(regex, message?);
email(message?);
phone(message?);
custom(fn);
4. Form Submission

MakeForm includes a submission helper.

const submit = form.handleSubmit((values) => {
  console.log(values);
});

submit();

handleSubmit():

  1. Marks all fields as touched
  2. Runs validation
  3. Prevents invalid submission
  4. Returns strongly typed values

API Overview

Form State Engine

The core engine is framework agnostic.

import { createForm } from '@hnpsaga/makeform';

const form = createForm(schema);

Available APIs:

form.getValues();
form.getValue('name');

form.setValue('name', 'John');

form.validate();

form.reset();

form.markAllTouched();

form.handleSubmit(callback);

form.subscribe(listener);
React Integration
useForm

Creates and manages a form instance bound to React's rendering cycle.

const form = useForm(schema);
useField

Subscribe to a single field. Only re-renders when that field changes.

const name = useField(form, 'name');

Returns:

{
  value,
  errors,
  touched,
  dirty,
  setValue,
}
Dynamic Form Rendering
FormRenderer

Automatically renders a complete form.

<FormRenderer form={form} schema={schema} />

Supported field types:

Field
textField
textareaField
emailField
phoneField
passwordField
numberField
dateField
checkboxField
radioField
selectField
multiSelectField
customField
Renderer Overrides

Replace built-in field renderers with your own components.

<FormRenderer
  form={form}
  schema={schema}
  renderers={{
    text: CustomTextRenderer,
    email: CustomEmailRenderer,
  }}
/>

Useful for:

  • Design systems
  • Component libraries
  • Internal UI standards
Custom Renderers

Integrate third-party components such as rich text editors, date pickers, phone pickers, file uploads, typeahead inputs, and location pickers.

Schema:

const schema = {
  bio: customField<string>({
    component: 'richText',
  }),
};

Renderer:

<FormRenderer
  form={form}
  schema={schema}
  renderers={{
    custom: {
      richText: RichTextRenderer,
    },
  }}
/>

Custom renderers automatically participate in validation, state updates, dirty tracking, touched tracking, reset, and submission.

Field Renderer Overrides

Complete field-level overrides that replace the entire field presentation — label, error, layout, and input.

import type { FieldRendererProps, FieldRenderers, TextField } from '@hnpsaga/makeform';

function MuiTextRenderer({ id, field, fieldState }: FieldRendererProps<string, TextField>) {
  return (
    <TextField
      id={id}
      label={field.label}
      value={fieldState.value}
      onChange={(e) => fieldState.setValue(e.target.value)}
      error={fieldState.touched && fieldState.errors.length > 0}
      helperText={fieldState.touched ? fieldState.errors[0] : undefined}
      fullWidth
    />
  );
}

<FormRenderer
  form={form}
  schema={schema}
  fieldRenderers={{
    text: MuiTextRenderer,
  }}
/>;
Resolution Priority
fieldRenderers.text
        ↓
renderers.text
        ↓
builtInRenderers.text
When to Use
Extension Point Controls Use Case
fieldRenderers Label, error, input Design system integration
renderers Input only Custom input controls
Built-in Everything Default MakeForm UI
Default Theme

MakeForm ships with a clean default theme.

import '@hnpsaga/makeform/dist/styles/default.css';

Includes responsive layout, labels, inputs, selects, textareas, checkboxes, radio groups, and error states.

Styling Overrides

Customize styling without replacing renderers.

<FormRenderer
  form={form}
  schema={schema}
  classNames={{
    form: 'my-form',
    grid: 'my-grid',
    field: 'my-field',
    label: 'my-label',
    input: 'my-input',
    error: 'my-error',
  }}
/>

Default styles remain active. Custom classes are appended.

Architecture

MakeForm consists of five layers:

Schema System
      ↓
Type Inference
      ↓
Validation Engine
      ↓
Form State Engine
      ↓
React Rendering Layer

The form engine itself is framework agnostic. React integration is intentionally thin.


Status

Current Release: v0.1.2

This project is actively maintained and available on npm.


Development

A demo application is available in apps/demo/ demonstrating schema definition, validation, form state, submission, and dynamic rendering.

Running the Demo
# Build the library first
npm run build

# Navigate to the demo app
cd apps/demo

# Install dependencies
npm install

# Start the dev server
npm run dev
Demo Pages
  • Home — Introduction and navigation
  • Registration — Registration form with text, email, password, and checkbox fields
  • Profile — Profile form with text, textarea, phone, date, and select fields
  • Validation — Validation showcase with required, min, max, pattern, and custom validators
  • Features — Overview of MakeForm features
  • Styling — Styling showcase with default theme, custom classNames, and utility-style customization
  • Renderers — Demonstrates renderers (input replacement) and fieldRenderers (complete field replacement)
  • Advanced — Specialized input controls (rich text editor, rating, tag selector) via renderers
  • Material UI — Material UI integration via fieldRenderers
Contributing

Issues and pull requests are welcome.

Before submitting changes:

npm run lint
npm run typecheck
npm run test
npm run build

License

MIT

Keywords