@metadiv-studio/metaform
v0.4.0
Published
A powerful, flexible React form component built with React Hook Form, Zod validation, and TypeScript. MetaForm provides a declarative way to create complex forms with automatic validation, responsive layouts, and customizable rendering.
Downloads
273
Readme
@metadiv-studio/metaform
A powerful, flexible React form component built with React Hook Form, Zod validation, and TypeScript. MetaForm provides a declarative way to create complex forms with automatic validation, responsive layouts, and customizable rendering.
🚀 Installation
npm install @metadiv-studio/metaform📦 Package Description
MetaForm is a comprehensive form solution that combines the power of:
- React Hook Form for efficient form state management
- Zod for runtime validation and type safety
- MetaGrid for responsive form layouts
- TypeScript for full type safety and IntelliSense
Key features:
- ✨ Declarative form definition - Define forms using simple configuration objects
- 🔒 Automatic validation - Built-in Zod schema validation
- 📱 Responsive layouts - Automatic responsive grid system
- 🎨 Customizable rendering - Render any React component as form fields
- 🔄 Form groups - Organize fields into logical sections
- 💡 Tooltips and help text - Built-in support for field descriptions
- 👁️ Conditional visibility - Show/hide fields based on form state
- 🎯 Type safety - Full TypeScript support with inferred types
🛠️ Usage
Basic Example
import MetaForm from '@metadiv-studio/metaform';
import { z } from 'zod';
import { Input } from '@metadiv-studio/input';
// Define your form schema
const userSchema = z.object({
firstName: z.string().min(2, 'First name must be at least 2 characters'),
lastName: z.string().min(2, 'Last name must be at least 2 characters'),
email: z.string().email('Invalid email address'),
});
// Define form items
const formItems = [
{
name: 'firstName',
label: 'First Name',
required: true,
render: (field) => (
<Input
{...field}
placeholder="Enter first name"
/>
),
},
{
name: 'lastName',
label: 'Last Name',
required: true,
render: (field) => (
<Input
{...field}
placeholder="Enter last name"
/>
),
},
{
name: 'email',
label: 'Email',
required: true,
tooltip: 'We will use this for account notifications',
render: (field) => (
<Input
{...field}
type="email"
placeholder="Enter email address"
/>
),
},
];
function UserForm() {
const handleSubmit = async (values: z.infer<typeof userSchema>) => {
console.log('Form values:', values);
// Handle form submission
};
return (
<MetaForm
schema={userSchema}
items={formItems}
onSubmit={handleSubmit}
saveButton={true}
saveButtonText="Create User"
/>
);
}Advanced Example with Form Groups
import MetaForm from '@metadiv-studio/metaform';
import { z } from 'zod';
import { Input } from '@metadiv-studio/input';
import { Select } from '@metadiv-studio/select';
const profileSchema = z.object({
personalInfo: z.object({
firstName: z.string().min(2),
lastName: z.string().min(2),
dateOfBirth: z.string(),
}),
contactInfo: z.object({
email: z.string().email(),
phone: z.string().optional(),
address: z.string(),
}),
preferences: z.object({
newsletter: z.boolean(),
notifications: z.boolean(),
theme: z.enum(['light', 'dark', 'auto']),
}),
});
const formItems = [
{
label: 'Personal Information',
items: [
{
name: 'personalInfo.firstName',
label: 'First Name',
required: true,
render: (field) => <Input {...field} placeholder="First name" />,
},
{
name: 'personalInfo.lastName',
label: 'Last Name',
required: true,
render: (field) => <Input {...field} placeholder="Last name" />,
},
{
name: 'personalInfo.dateOfBirth',
label: 'Date of Birth',
render: (field) => <Input {...field} type="date" />,
},
],
},
{
label: 'Contact Information',
items: [
{
name: 'contactInfo.email',
label: 'Email',
required: true,
tooltip: 'Primary contact email',
render: (field) => <Input {...field} type="email" placeholder="Email" />,
},
{
name: 'contactInfo.phone',
label: 'Phone',
render: (field) => <Input {...field} placeholder="Phone number" />,
},
{
name: 'contactInfo.address',
label: 'Address',
render: (field) => <Input {...field} placeholder="Full address" />,
},
],
},
{
label: 'Preferences',
items: [
{
name: 'preferences.newsletter',
label: 'Newsletter Subscription',
render: (field) => (
<input
type="checkbox"
checked={field.value}
onChange={(e) => field.onChange(e.target.checked)}
/>
),
},
{
name: 'preferences.theme',
label: 'Theme',
render: (field) => (
<Select
value={field.value}
onValueChange={field.onChange}
options={[
{ value: 'light', label: 'Light' },
{ value: 'dark', label: 'Dark' },
{ value: 'auto', label: 'Auto' },
]}
/>
),
},
],
},
];
function ProfileForm() {
const handleSubmit = async (values: z.infer<typeof profileSchema>) => {
console.log('Profile updated:', values);
};
return (
<MetaForm
schema={profileSchema}
items={formItems}
onSubmit={handleSubmit}
saveButton={true}
saveButtonText="Update Profile"
/>
);
}Example with Data Fetching
import MetaForm from '@metadiv-studio/metaform';
import { z } from 'zod';
import { useEffect } from 'react';
const productSchema = z.object({
name: z.string().min(1, 'Product name is required'),
price: z.number().min(0, 'Price must be positive'),
description: z.string().optional(),
});
function ProductEditForm({ productId }: { productId: string }) {
const fetchProduct = async (id: string) => {
// Fetch product data from API
const response = await fetch(`/api/products/${id}`);
const product = await response.json();
// Update form with fetched data
form.reset(product);
};
const handleSubmit = async (values: z.infer<typeof productSchema>) => {
// Update product via API
await fetch(`/api/products/${productId}`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(values),
});
};
return (
<MetaForm
schema={productSchema}
items={[
{
name: 'name',
label: 'Product Name',
required: true,
render: (field) => <Input {...field} placeholder="Product name" />,
},
{
name: 'price',
label: 'Price',
required: true,
render: (field) => (
<Input
{...field}
type="number"
step="0.01"
placeholder="0.00"
/>
),
},
{
name: 'description',
label: 'Description',
render: (field) => (
<textarea
{...field}
placeholder="Product description"
rows={4}
/>
),
},
]}
fetchId={productId}
fetch={fetchProduct}
onSubmit={handleSubmit}
saveButton={true}
saveButtonText="Update Product"
/>
);
}🔧 API Reference
MetaForm Props
| Prop | Type | Description |
|------|------|-------------|
| schema | z.ZodType | Zod schema for form validation |
| items | FormItem[] | Array of form field definitions |
| defaultValues | Partial<T> | Initial form values |
| loading | boolean | Show loading state for all fields |
| saveButton | boolean | Show save button |
| saveButtonText | string | Custom save button text |
| cancelButtonText | string | Custom cancel button text |
| onSubmit | (values: T) => Promise<void> | Form submission handler |
| onCancel | () => void | Cancel button handler |
| onValuesChange | (values: T) => void | Form values change handler |
| fetch | (id?: any, form?: UseFormReturn) => Promise<void> | Data fetching function |
| fetchId | any | ID for data fetching |
| fetchError | (error: any) => void | Error handling for fetch operations |
| form | UseFormReturn | External form instance |
FormItem Interface
interface FormItem<T> {
name?: Path<T>; // Field name in form data
label?: React.ReactNode; // Field label
required?: boolean; // Required field indicator
tooltip?: string; // Help text/tooltip
className?: string; // Custom CSS classes
visible?: (form: UseFormReturn) => boolean; // Conditional visibility
render?: (field: ControllerRenderProps, form: UseFormReturn) => React.ReactNode; // Field renderer
items?: FormItem<T>[]; // Nested form items for groups
}🎨 Styling
MetaForm uses Tailwind CSS for styling. To use the default styles, ensure Tailwind CSS is included in your project and add the following to your tailwind.config.js:
module.exports = {
content: [
// ... other content paths
"./node_modules/@metadiv-studio/**/*.{js,ts,jsx,tsx}",
],
// ... rest of config
}📚 Dependencies
This package has the following peer dependencies:
react^18react-dom^18
And requires these packages to be installed in your project:
@hookform/resolverszodreact-hook-form
🤝 Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
📄 License
This project is licensed under the UNLICENSED license.
