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.
npm install @metadiv-studio/metaformA 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.
``bash`
npm install @metadiv-studio/metaform
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
`tsx
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) => (
{...field}
placeholder="Enter first name"
/>
),
},
{
name: 'lastName',
label: 'Last Name',
required: true,
render: (field) => (
{...field}
placeholder="Enter last name"
/>
),
},
{
name: 'email',
label: 'Email',
required: true,
tooltip: 'We will use this for account notifications',
render: (field) => (
{...field}
type="email"
placeholder="Enter email address"
/>
),
},
];
function UserForm() {
const handleSubmit = async (values: z.infer
console.log('Form values:', values);
// Handle form submission
};
return (
items={formItems}
onSubmit={handleSubmit}
saveButton={true}
saveButtonText="Create User"
/>
);
}
`
`tsx
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) => ,
},
{
name: 'personalInfo.lastName',
label: 'Last Name',
required: true,
render: (field) => ,
},
{
name: 'personalInfo.dateOfBirth',
label: 'Date of Birth',
render: (field) => ,
},
],
},
{
label: 'Contact Information',
items: [
{
name: 'contactInfo.email',
label: 'Email',
required: true,
tooltip: 'Primary contact email',
render: (field) => ,
},
{
name: 'contactInfo.phone',
label: 'Phone',
render: (field) => ,
},
{
name: 'contactInfo.address',
label: 'Address',
render: (field) => ,
},
],
},
{
label: 'Preferences',
items: [
{
name: 'preferences.newsletter',
label: 'Newsletter Subscription',
render: (field) => (
type="checkbox"
checked={field.value}
onChange={(e) => field.onChange(e.target.checked)}
/>
),
},
{
name: 'preferences.theme',
label: 'Theme',
render: (field) => (
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
console.log('Profile updated:', values);
};
return (
items={formItems}
onSubmit={handleSubmit}
saveButton={true}
saveButtonText="Update Profile"
/>
);
}
`
`tsx
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
// Update product via API
await fetch(/api/products/${productId}, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(values),
});
};
return (
items={[
{
name: 'name',
label: 'Product Name',
required: true,
render: (field) => ,
},
{
name: 'price',
label: 'Price',
required: true,
render: (field) => (
{...field}
type="number"
step="0.01"
placeholder="0.00"
/>
),
},
{
name: 'description',
label: 'Description',
render: (field) => (
{...field}
placeholder="Product description"
rows={4}
/>
),
},
]}
fetchId={productId}
fetch={fetchProduct}
onSubmit={handleSubmit}
saveButton={true}
saveButtonText="Update Product"
/>
);
}
`
| Prop | Type | Description |
|------|------|-------------|
| schema | z.ZodType | Zod schema for form validation |items
| | FormItem[] | Array of form field definitions |defaultValues
| | Partial | 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 | Form submission handler |onCancel
| | () => void | Cancel button handler |onValuesChange
| | (values: T) => void | Form values change handler |fetch
| | (id?: any, form?: UseFormReturn) => Promise | Data fetching function |fetchId
| | any | ID for data fetching |fetchError
| | (error: any) => void | Error handling for fetch operations |form
| | UseFormReturn | External form instance |
`typescript`
interface FormItem
name?: Path
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
}
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:
`js`
module.exports = {
content: [
// ... other content paths
"./node_modules/@metadiv-studio/*/.{js,ts,jsx,tsx}",
],
// ... rest of config
}
This package has the following peer dependencies:
- react ^18react-dom
- ^18
And requires these packages to be installed in your project:
- @hookform/resolverszod
- react-hook-form`
-
Contributions are welcome! Please feel free to submit a Pull Request.
This project is licensed under the UNLICENSED license.