AutoForm — dynamic form builder with React Hook Form, Zod and built-in shadcn-style UI
npm install @taha-ui-dev/auto-formBuilt with React Hook Form, Zod, and shadcn-style UI components — all bundled and ready to use.
Create complex, fully validated forms using a single schema + config object with zero boilerplate.
---
- ✔ Automatic form generation (schema + config-only)
- ✔ Built on React Hook Form
- ✔ Zod validation included
- ✔ All common fields included (text, select, radio, date, file upload…)
- ✔ File upload with preview, drag & drop, multi-file support
- ✔ Multi-select (normal + popover)
- ✔ Localized fields (e.g., en, ar)
- ✔ Dynamic variant builder (field arrays)
- ✔ Rich text editor with formatting options
- ✔ Date range picker with customizable locale and timezone
- ✔ Password field with toggle visibility
- ✔ Shadcn-style components included
- ✔ Optimized & fully typed for TypeScript
---
``bash`
npm install @taha-ui-dev/auto-form@latestor
pnpm add @taha-ui-dev/auto-form@latestor
yarn add @taha-ui-dev/auto-form@latest
Make sure you have these peer dependencies installed:
`bash`
npm install zod@^4.0.0or
pnpm add zod@^4.0.0or
yarn add zod@^4.0.0
---
`ts
import { z } from "zod";
export const formSchema = z.object({
// Basic fields
name: z.string().optional(),
email: z.string().optional(),
phone: z.string().optional(),
password: z.string().optional(),
toggle_password: z.string().optional(),
// Date fields
single_date: z.string().optional(),
range_date: z
.object({
from: z.string().optional(),
to: z.string().optional(),
})
.optional(),
// Selection fields
select: z.string().optional(),
multi_select: z.array(z.string()).optional(),
radio: z.string().optional(),
checkbox: z.boolean().optional(),
// Rich content
textarea: z.string().optional(),
rich_text: z.string().optional(),
// File uploads
file: z.instanceof(File).optional(),
files: z.array(z.instanceof(File)).max(10).optional(),
// Advanced fields
localized_text: z
.object({
en: z.string(),
ar: z.string(),
})
.optional(),
// Dynamic fields array
variants: z
.array(
z.object({
variant1: z.string().optional(),
variant2: z.string().optional(),
})
)
.optional(),
});
export type FormValues = z.infer
`
---
`ts
import type { FieldConfig } from "@taha-ui-dev/auto-form";
export const formFields: FieldConfig[] = [
// Basic text inputs
{
name: "name",
label: "Full Name",
field_type: "text",
placeholder: "Enter your full name",
},
{
name: "email",
label: "Email",
field_type: "text",
},
{
name: "phone",
label: "Phone Number",
field_type: "phone",
},
// Password field
{
name: "password",
label: "Password",
field_type: "password",
},
// Selection fields
{
name: "select",
label: "Select Option",
field_type: "select",
select_options: [
{ value: "option1", label: "Option 1" },
{ value: "option2", label: "Option 2" },
],
},
{
name: "multi_select",
label: "Multi Select",
field_type: "multi_select",
options_className: "border-0",
multi_select_options: [
{ value: "option1", label: "Option 1" },
{ value: "option2", label: "Option 2" },
],
},
{
name: "radio",
label: "Radio Group",
field_type: "radio",
radio_options: [
{ value: "option1", label: "Option 1" },
{ value: "option2", label: "Option 2" },
],
},
{
name: "checkbox",
label: "Checkbox",
field_type: "checkbox",
options: {
classes: {
wrapper: "h-full flex items-center gap-2",
},
},
},
// Date fields
{
name: "single_date",
label: "Single Date",
field_type: "single_date",
},
{
name: "range_date",
label: "Date Range",
field_type: "range_date",
date_config: {
locale: "en",
timeZone: "Asia/Riyadh",
},
},
// Rich content
{
name: "textarea",
label: "Textarea",
field_type: "textarea",
},
{
name: "rich_text",
label: "Rich Text",
field_type: "rich_text",
className: "col-span-2",
},
// File uploads
{
name: "file",
label: "Single File Upload",
field_type: "file",
image_options: {
accept: "image/*",
maxSizeMb: 1,
maxFiles: 1,
},
},
{
name: "files",
label: "Multiple Files",
field_type: "files",
image_options: {
multiple_grid: "grid-cols-2",
accept: "image/*",
multiple: true,
maxSizeMb: 1,
maxFiles: 10,
},
},
// Advanced fields
{
name: "localized_text",
label: "Localized Text",
field_type: "localized_text",
},
// Dynamic fields array
{
name: "variants",
label: "Product Variants",
field_type: "variants",
variants: [
{
name: "variant1",
label: "Variant 1",
field_type: "text",
},
{
name: "variant2",
label: "Variant 2",
field_type: "text",
},
],
},
];
`
---
`tsx
// Add this line at the top of your component if you are using Next.js
"use client";
import { AutoForm } from "@taha-ui-dev/auto-form";
import { formSchema, formFields, type FormValues } from "./form-config";
export default function ExampleForm() {
const handleSubmit = async (values: FormValues) => {
// Handle form submission
console.log("Form submitted:", values);
// Example: await api.submitForm(values);
};
// Set default values for controlled inputs
const defaultValues = {
name: "",
email: "",
phone: "",
password: "",
toggle_password: "",
select: "",
multi_select: [],
checkbox: false,
textarea: "",
radio: "",
single_date: "",
range_date: { from: "", to: "" },
variants: [],
file: undefined,
files: [],
rich_text: "",
localized_text: { en: "", ar: "" },
};
return (
🎨 Styling
This package uses Tailwind CSS for styling. Make sure you have Tailwind CSS set up in your project. The components follow the shadcn/ui design system.
$3
Import the base styles in your application's main file:
`ts
@import "@taha-ui-dev/auto-form/style.css";
`🧱 Supported Field Types
| Field Type | Component | Description |
| ---------------- | --------------------- | -------------------------------------------------------------------- |
|
text | Text input | Standard text input field |
| password | Password input | Password field with toggle visibility |
| phone | Phone input | Phone number input with international country selector |
| select | Dropdown/select | Single selection dropdown |
| multi_select | Multi-select | Multiple selection with checkboxes or chips |
| radio | Radio group | Radio button group for single selection |
| checkbox | Checkbox | Single checkbox or boolean toggle |
| textarea | Textarea | Multi-line text input |
| rich_text | Rich text editor | WYSIWYG editor with formatting options |
| single_date | Date picker | Single date selection with calendar |
| range_date | Date range picker | Select a date range with start and end dates |
| file | Single file upload | File upload with preview and drag & drop |
| files | Multiple file upload | Multiple file upload with grid layout |
| localized_text | Localized text fields | Multiple language text inputs in a single field |
| variants | Dynamic field array | Repeatable group of fields for product variants or similar use cases |---
📁 File Upload Fields
$3
`ts
// Schema
gallery: z.instanceof(File).optional(),// Default value
gallery: undefined,
// Field config
{
name: "gallery",
label: "Upload Image",
field_type: "file",
image_options: {
accept: "image/*",
maxSizeMb: 5,
maxFiles: 1
}
}
`$3
`ts
// Schema
gallery: z.array(z.instanceof(File)).max(10).optional(),// Default value
gallery: [],
// Field config
{
name: "gallery",
label: "Image Gallery",
field_type: "files",
image_options: {
multiple_grid: "grid-cols-3 gap-4", // Customize grid layout
accept: "image/*", // Allowed file types
multiple: true, // Allow multiple files
maxSizeMb: 5, // Max file size in MB
maxFiles: 10 // Maximum number of files
}
}
`$3
- Drag & Drop: Intuitive file upload interface
- Image Previews: Thumbnails for uploaded images
- File Type Icons: Automatic icons for different file types (PDF, DOC, ZIP, etc.)
- Validation:
- File type restrictions (
accept prop)
- Maximum file size (maxSizeMb)
- Maximum number of files (maxFiles)
- Responsive Grid: Customizable grid layout for multiple files
- Preview & Remove: Preview and remove individual files---
🌍 Localized Fields
Create multi-language text inputs with a single field. The component automatically generates language tabs and handles the nested data structure.
$3
`ts
// Schema
title: z.object({
en: z.string(),
ar: z.string()
}).optional(),// Default values
{
title: { en: "", ar: "" },
}
// Field config
{
name: "title",
label: "Title",
field_type: "localized_text",
locales: ["en", "ar"] // Add more language codes as needed
}
`$3
- Multiple Languages: Support for any number of languages
- Tabbed Interface: Easy switching between language inputs
- RTL Support: Automatic text direction for RTL languages like Arabic
- Consistent Data Structure: Outputs a clean object with language codes as keys
$3
``ts
`ts
// Schema
description: z.string().optional(),// Default value
{
description: "",
}
// Field config
{
name: "description",
label: "Description",
field_type: "rich_text",
className: "min-h-[200px]" // Set minimum height
}
``$3
- Rich Text Formatting: Bold, italic, headings, lists, quotes, and more
- Media Support: Embed images and videos
- Code Blocks: Syntax highlighting for code snippets
- Tables: Create and edit tables
- Mentions & Hashtags: Coming soon
- Word Count: Track content length
- Auto-save: Draft saving functionality
$3
`ts
{
name: "content",
label: "Article Content",
field_type: "rich_text",
className: "min-h-[400px] border rounded-lg p-4",
editorOptions: {
placeholder: "Start writing your article here...",
extensions: [
// Add custom extensions
],
onUpdate: (editor) => {
// Handle editor updates
console.log(editor.getHTML());
}
},
// Character/word limit
maxLength: 5000,
showWordCount: true
}
`$3
`ts
// Schema with validation
description: z
.string()
.min(50, "Description must be at least 50 characters")
.max(5000, "Description cannot exceed 5000 characters"),
// Conditional validation based on another field
z.object({
hasDescription: z.boolean(),
description: z.string().superRefine((val, ctx) => {
if (ctx.parent.hasDescription && !val) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: "Description is required when hasDescription is true",
});
}
}),
});
`$3
`css
/ Customize the rich text editor appearance /
.rich-text-editor {
@apply border rounded-lg overflow-hidden; / Toolbar /
.ProseMirror-toolbar {
@apply bg-gray-50 border-b p-2 flex flex-wrap gap-1;
}
/ Content area /
.ProseMirror {
@apply p-4 min-h-[200px] focus:outline-none;
}
/ Active button state /
button.is-active {
@apply bg-gray-200;
}
}
`$3
You can extend the rich text editor with additional Tiptap extensions:
`ts
import { Extension } from "@tiptap/core";
import { ReactRenderer } from "@tiptap/react";export const CustomExtension = Extension.create({
name: "customExtension",
// Extension configuration
});
// Then use it in your field config
{
field_type: "rich_text",
extensions: [CustomExtension],
// ...other options
}
`---
✔ AutoForm Props
`ts
interface AutoFormProps> {
schema: T;
mode?: "onSubmit" | "onChange" | "onBlur" | "all" | "onTouched";
is_dev?: boolean;
fields: FieldConfig[];
defaultValues?: DefaultValues>;
onSubmit: (values: z.infer) => Promise | void;
className?: string;
form_id?: string;
buttons?: {
className?: string;
submit?: {
text?: string;
className?: string;
};
reset?: {
text?: string;
className?: string;
};
};
}
`---
🎨 Styling
This package ships with lightweight shadcn-style components, including:
- Button
- Input
- Select
- Popover
- Radio
- Checkbox
- Calendar
- Separator
- Textarea
- ScrollArea
- Spinner
- Input Group
You do not need to install shadcn/ui.
---
📦 Bundle Output
This library generates:
| Format | File |
| ------ | ----------------------- |
| ES |
dist/auto-form.es.js |
| CJS | dist/auto-form.cjs.js |
| Types | dist/index.d.ts` |---
PRs and feedback are welcome.
---
MIT © Taha