json-form-craft
v1.0.2
Published
A lightweight, extensible, schema-driven React form builder that generates fully functional forms from JSON.
Downloads
441
Maintainers
Readme
json-form-craft
A lightweight, extensible, schema-driven React form builder that generates fully functional, accessible forms from JSON schemas powered by React Hook Form.
Features
- ⚡ Zero Runtime Overhead: Built on top of
react-hook-formwith zero extra UI framework dependencies. - 📦 Tree-shakeable & Dual Bundle: Ships ESM and CommonJS declarations generated automatically.
- 🎨 Fully Customisable & Accessible: Includes clean default styles, ARIA attributes, keyboard navigation, and full custom
classNamecontrol. - 🧩 Extensible: Seamlessly register custom field components (e.g. Rich Text, Rating Stars, Custom Sliders).
- 🛠️ 18+ Built-in Field Types: Supports text, email, password, number, textarea, select, radio, checkbox, switch, date, time, datetime, file, hidden, color, range, url, tel.
- 📘 TypeScript Native: Complete type definitions exported for all schemas, props, and hooks.
Installation
npm install json-form-craft react-hook-form
# or
yarn add json-form-craft react-hook-form
# or
pnpm add json-form-craft react-hook-formNote: react and react-dom (>=18.0.0) are peer dependencies.
Quick Start
Import FormBuilder and pass your JSON schema:
import { FormBuilder, FieldSchema } from "json-form-craft";
import "json-form-craft/styles.css"; // Optional clean default styling
const schema: FieldSchema[] = [
{
type: "text",
name: "name",
label: "Name",
placeholder: "Enter your name",
required: true,
},
{
type: "email",
name: "email",
label: "Email",
placeholder: "Enter your email",
},
{
type: "select",
name: "country",
label: "Country",
options: [
{ label: "India", value: "india" },
{ label: "USA", value: "usa" },
],
},
];
function App() {
return (
<FormBuilder
schema={schema}
onSubmit={(values) => console.log("Submitted values:", values)}
/>
);
}
export default App;Supported Field Types
| Field Type | Rendered Element | Notes |
| :--- | :--- | :--- |
| text | <input type="text"> | Standard single-line text input |
| email | <input type="email"> | Includes built-in email regex validation |
| password | <input type="password"> | Secure password field |
| number | <input type="number"> | Converts value to number automatically |
| textarea | <textarea> | Supports rows and cols schema props |
| select | <select> | Supports options array and multiple mode |
| radio | <input type="radio"> | Radio group based on options |
| checkbox | <input type="checkbox"> | Single boolean checkbox or checkbox array group |
| switch | <input type="checkbox" role="switch"> | Styled toggle switch |
| date | <input type="date"> | Date picker with min and max constraints |
| time | <input type="time"> | Time picker |
| datetime | <input type="datetime-local"> | Datetime picker |
| file | <input type="file"> | Supports accept and multiple |
| hidden | <input type="hidden"> | Hidden form field |
| color | <input type="color"> | Color picker |
| range | <input type="range"> | Slider range input |
| url | <input type="url"> | Includes built-in URL format validation |
| tel | <input type="tel"> | Telephone input |
Validation Examples
Validation rules can be defined using shorthand attributes (like required: true) or fine-grained validation objects (validation: { minLength: { value: 8, message: "Min 8 chars" } }).
[
{
"type": "text",
"name": "username",
"label": "Username",
"required": "Username is mandatory",
"validation": {
"minLength": {
"value": 3,
"message": "Username must be at least 3 characters"
},
"maxLength": 20
}
},
{
"type": "number",
"name": "age",
"label": "Age",
"validation": {
"min": { "value": 18, "message": "Must be 18+" },
"max": 99
}
},
{
"type": "text",
"name": "confirmPassword",
"label": "Confirm Password",
"validation": {
"custom": "(val, allValues) => val === allValues.password || 'Passwords do not match'"
}
}
]Custom Synchronous / Asynchronous Validators in JS/TS:
const schema: FieldSchema[] = [
{
type: "text",
name: "username",
label: "Username",
validation: {
custom: async (value) => {
const isAvailable = await checkUsernameAvailable(value);
return isAvailable || "Username is already taken";
},
},
},
];Custom Field Components (Extensibility)
You can register custom components for any custom type string using the customFields prop:
import { FormBuilder, CustomFieldProps } from "json-form-craft";
// 1. Define your custom component
const RatingField: React.FC<CustomFieldProps> = ({ field, value, onChange }) => {
return (
<div>
<label>{field.label}</label>
<button type="button" onClick={() => onChange?.(5)}>
Selected: {value || 0} ⭐
</button>
</div>
);
};
// 2. Use in schema
const schema = [
{
type: "rating", // Custom type
name: "score",
label: "Customer Score",
},
];
// 3. Register customFields
<FormBuilder
schema={schema}
customFields={{ rating: RatingField }}
onSubmit={(values) => console.log(values)}
/>;API Documentation
FormBuilder Component Props
interface FormBuilderProps {
/** Array of field schema definitions */
schema: FieldSchema[];
/** Default initial form values */
defaultValues?: Record<string, any>;
/** Callback on valid form submission */
onSubmit: (values: Record<string, any>) => void | Promise<void>;
/** Callback fired whenever any form value changes */
onChange?: (values: Record<string, any>) => void;
/** Callback fired when validation fails on submit */
onError?: (errors: Record<string, any>) => void;
/** Disable all form fields */
disabled?: boolean;
/** Form element CSS class */
className?: string;
/** Submit button customization */
submitButton?:
| React.ReactNode
| {
text?: string;
className?: string;
disabled?: boolean;
hidden?: boolean;
};
/** Custom field components mapping */
customFields?: Record<string, CustomFieldComponent>;
/** Extra children inside form */
children?: React.ReactNode;
}Headless Hook: useFormBuilder
For full control over form layout, use useFormBuilder:
import { useFormBuilder } from "json-form-craft";
function CustomLayoutForm() {
const { form, handleSubmit, renderFields } = useFormBuilder({
schema: mySchema,
onChange: (values) => console.log("Live values:", values),
});
return (
<form onSubmit={handleSubmit((data) => console.log(data))}>
<div className="grid grid-cols-2">
{renderFields()}
</div>
<button type="submit">Submit</button>
</form>
);
}TypeScript Usage
All types are exported directly from json-form-craft:
import type {
FieldSchema,
FieldType,
BuiltInFieldType,
FieldOption,
FieldValidationRules,
FormBuilderProps,
CustomFieldProps,
} from "json-form-craft";Styling Guide
json-form-craft ships with optional clean CSS (json-form-craft/styles.css). You can customize the look via CSS variables or by passing custom class names per field:
CSS Variables Customization
:root {
--jfk-primary: #6366f1;
--jfk-primary-hover: #4f46e5;
--jfk-border: #e2e8f0;
--jfk-radius: 0.5rem;
}ClassName Schema Overrides
{
"type": "text",
"name": "company",
"label": "Company Name",
"containerClassName": "my-field-container",
"labelClassName": "my-label-style",
"inputClassName": "my-input-style",
"errorClassName": "my-error-style"
}Publishing & Build Instructions
Build Library
npm run buildCompiles TypeScript and outputs distribution bundles in dist/:
dist/index.js(ES Module)dist/index.cjs(CommonJS)dist/index.d.ts(TypeScript Declaration)dist/style.css(CSS Bundle)
Run Unit Tests
npm run testPublish to NPM
npm publish --access publicRepository
License
MIT © json-form-craft
