@mehdashti/forms
v1.0.0
Published
Enterprise-grade form management with validation, error handling, autosave, and advanced components
Maintainers
Readme
@mehdashti/forms
Comprehensive form validation and management for Smart Platform
Built on React Hook Form + Zod with smart defaults and seamless integration with @mehdashti/ui components.
Features
- ✅ React Hook Form + Zod - Type-safe validation with automatic error handling
- ✅ Real-time Validation - Validate on change by default
- ✅ Smart Defaults - Sensible defaults based on APISmith patterns
- ✅ Form Field Components - Pre-built field components with error display
- ✅ Layout Components - Ready-to-use 1, 2, 3, 4 column layouts (NEW!)
- ✅ Form Sections - Organize forms with titles and descriptions (NEW!)
- ✅ Advanced Grids - Custom column spans with FormGrid (NEW!)
- ✅ Field Arrays - Dynamic form fields support
- ✅ TypeScript First - Full type safety with excellent IntelliSense
- ✅ @mehdashti/ui Integration - Works seamlessly with platform UI components
- ✅ Lightweight - Minimal bundle size with tree-shaking support
Installation
pnpm add @mehdashti/forms react-hook-form zod @hookform/resolversPeer Dependencies:
pnpm add react react-dom @mehdashti/ui @mehdashti/contractsQuick Start
Basic Form
import { useSmartForm, FormField, z } from '@mehdashti/forms'
const loginSchema = z.object({
email: z.string().email('Invalid email address'),
password: z.string().min(8, 'Password must be at least 8 characters'),
})
function LoginForm() {
const form = useSmartForm({
schema: loginSchema,
onSubmit: async (data) => {
await loginUser(data)
},
})
return (
<form onSubmit={form.handleSubmit}>
<FormField
name="email"
label="Email"
type="email"
required
control={form.control}
placeholder="Enter your email"
/>
<FormField
name="password"
label="Password"
type="password"
required
control={form.control}
/>
<button type="submit" disabled={form.isSubmitting}>
{form.isSubmitting ? 'Loading...' : 'Login'}
</button>
</form>
)
}Form with Select
import { useSmartForm, FormField, FormSelect, z } from '@mehdashti/forms'
const connectionSchema = z.object({
name: z.string().min(1, 'Name is required'),
type: z.enum(['oracle', 'mssql', 'postgresql']),
host: z.string().min(1, 'Host is required'),
port: z.number().min(1).max(65535),
})
function ConnectionForm() {
const form = useSmartForm({
schema: connectionSchema,
defaultValues: {
type: 'oracle',
port: 1521,
},
onSubmit: async (data) => {
await createConnection(data)
},
})
return (
<form onSubmit={form.handleSubmit}>
<FormField
name="name"
label="Connection Name"
required
control={form.control}
/>
<FormSelect
name="type"
label="Database Type"
required
control={form.control}
options={[
{ value: 'oracle', label: 'Oracle' },
{ value: 'mssql', label: 'MS SQL Server' },
{ value: 'postgresql', label: 'PostgreSQL' },
]}
/>
<FormField
name="host"
label="Host"
required
control={form.control}
placeholder="192.168.1.100"
/>
<FormField
name="port"
label="Port"
type="number"
required
control={form.control}
/>
<button type="submit">Save Connection</button>
</form>
)
}Dynamic Field Arrays
import { useSmartForm, useSmartFieldArray, FormField, z } from '@mehdashti/forms'
const orderSchema = z.object({
customerName: z.string().min(1),
items: z.array(
z.object({
name: z.string().min(1, 'Item name is required'),
quantity: z.number().min(1, 'Quantity must be at least 1'),
})
),
})
function OrderForm() {
const form = useSmartForm({
schema: orderSchema,
defaultValues: {
items: [{ name: '', quantity: 1 }],
},
onSubmit: async (data) => {
await createOrder(data)
},
})
const { fields, append, remove } = useSmartFieldArray({
name: 'items',
control: form.control,
defaultValue: { name: '', quantity: 1 },
})
return (
<form onSubmit={form.handleSubmit}>
<FormField
name="customerName"
label="Customer Name"
required
control={form.control}
/>
<div>
<h3>Order Items</h3>
{fields.map((field, index) => (
<div key={field.id} className="flex gap-2">
<FormField
name={`items.${index}.name`}
label="Item Name"
required
control={form.control}
/>
<FormField
name={`items.${index}.quantity`}
label="Quantity"
type="number"
required
control={form.control}
/>
<button type="button" onClick={() => remove(index)}>
Remove
</button>
</div>
))}
<button type="button" onClick={() => append()}>
Add Item
</button>
</div>
<button type="submit">Create Order</button>
</form>
)
}Custom Validation
import { useSmartForm, FormField, z } from '@mehdashti/forms'
const registrationSchema = z
.object({
email: z.string().email(),
password: z.string().min(8),
confirmPassword: z.string(),
})
.refine((data) => data.password === data.confirmPassword, {
message: "Passwords don't match",
path: ['confirmPassword'],
})
function RegistrationForm() {
const form = useSmartForm({
schema: registrationSchema,
onSubmit: async (data) => {
await registerUser(data)
},
onError: (errors) => {
console.error('Validation errors:', errors)
},
})
return (
<form onSubmit={form.handleSubmit}>
<FormField
name="email"
label="Email"
type="email"
required
control={form.control}
/>
<FormField
name="password"
label="Password"
type="password"
required
control={form.control}
helpText="Must be at least 8 characters"
/>
<FormField
name="confirmPassword"
label="Confirm Password"
type="password"
required
control={form.control}
/>
<button type="submit">Register</button>
</form>
)
}Form Layouts
Pre-built layout components for quick form creation.
1-Column Layout
import { useSmartForm, FormField, FormLayout, z } from '@mehdashti/forms'
const form = useSmartForm({ schema, onSubmit: async (data) => {} })
return (
<form onSubmit={form.handleSubmit}>
<FormLayout columns={1}>
<FormField name="email" label="Email" required control={form.control} />
<FormField name="password" label="Password" type="password" required control={form.control} />
</FormLayout>
</form>
)2-Column Layout
import { FormLayout } from '@mehdashti/forms'
<FormLayout columns={2}>
<FormField name="firstName" label="First Name" required control={form.control} />
<FormField name="lastName" label="Last Name" required control={form.control} />
<FormField name="email" label="Email" required control={form.control} />
<FormField name="phone" label="Phone" required control={form.control} />
</FormLayout>3-Column Layout
<FormLayout columns={3}>
<FormField name="name" label="Connection Name" required control={form.control} />
<FormSelect name="type" label="Type" required control={form.control} options={types} />
<FormField name="host" label="Host" required control={form.control} />
<FormField name="port" label="Port" type="number" required control={form.control} />
<FormField name="database" label="Database" required control={form.control} />
<FormField name="username" label="Username" required control={form.control} />
</FormLayout>Form Sections
Organize forms into sections with titles:
import { FormSection } from '@mehdashti/forms'
<div className="space-y-8">
<FormSection title="Personal Information" description="Enter your details" columns={2}>
<FormField name="firstName" label="First Name" required control={form.control} />
<FormField name="lastName" label="Last Name" required control={form.control} />
</FormSection>
<FormSection title="Company Information" columns={2}>
<FormField name="company" label="Company" required control={form.control} />
<FormField name="position" label="Position" required control={form.control} />
</FormSection>
</div>Advanced Grid Layouts
Use FormGrid and FormGridItem for custom column spans:
import { FormGrid, FormGridItem } from '@mehdashti/forms'
<FormGrid>
{/* Full width */}
<FormGridItem colSpan="full">
<FormField name="title" label="Title" required control={form.control} />
</FormGridItem>
{/* 2 columns */}
<FormGridItem colSpan={1}>
<FormField name="firstName" label="First Name" required control={form.control} />
</FormGridItem>
<FormGridItem colSpan={1}>
<FormField name="lastName" label="Last Name" required control={form.control} />
</FormGridItem>
{/* Full width */}
<FormGridItem colSpan="full">
<FormField name="bio" label="Bio" control={form.control} />
</FormGridItem>
</FormGrid>See LAYOUT_EXAMPLES.md for complete examples.
API Reference
useSmartForm(options)
Main hook for form management.
Options:
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| schema | ZodSchema | required | Zod validation schema |
| onSubmit | function | required | Submit handler |
| onError | function | optional | Error handler |
| defaultValues | object | optional | Initial form values |
| realtimeValidation | boolean | true | Enable validation on change |
Returns:
| Property | Type | Description |
|----------|------|-------------|
| handleSubmit | function | Form submit handler |
| isSubmitting | boolean | Whether form is submitting |
| hasErrors | boolean | Whether form has validation errors |
| getError | function | Get error message for a field |
| control | Control | React Hook Form control |
| watch | function | Watch field values |
| setValue | function | Set field value |
| reset | function | Reset form |
| ... | | All React Hook Form methods |
<FormField>
Smart form field component with automatic error handling.
Props:
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| name | string | required | Field name |
| label | string | required | Field label |
| type | string | 'text' | Input type |
| required | boolean | false | Show required indicator |
| placeholder | string | optional | Placeholder text |
| helpText | string | optional | Help text below field |
| disabled | boolean | false | Disable field |
| control | Control | required | Form control |
| render | function | optional | Custom render function |
<FormSelect>
Select field component.
Props:
Same as FormField plus:
| Prop | Type | Description |
|------|------|-------------|
| options | SelectOption[] | Select options |
<FormCheckbox>
Checkbox field component.
Props:
| Prop | Type | Description |
|------|------|-------------|
| name | string | Field name |
| label | string | Checkbox label |
| description | string | Description text |
| required | boolean | Show required indicator |
| control | Control | Form control |
useSmartFieldArray(options)
Hook for managing dynamic field arrays.
Options:
| Option | Type | Description |
|--------|------|-------------|
| name | string | Field array name |
| control | Control | Form control |
| defaultValue | any | Default value for new items |
Returns:
| Property | Type | Description |
|----------|------|-------------|
| fields | array | Current field array |
| append | function | Add new field |
| remove | function | Remove field by index |
| insert | function | Insert field at index |
| move | function | Move field |
| swap | function | Swap two fields |
Integration with @mehdashti/ui
The form components are designed to work seamlessly with @mehdashti/ui. You can also use @mehdashti/ui components directly:
import { useSmartForm, z } from '@mehdashti/forms'
import { Button, Input, Label } from '@mehdashti/ui'
import { Controller } from 'react-hook-form'
function CustomForm() {
const form = useSmartForm({
schema: z.object({
name: z.string().min(1),
}),
onSubmit: async (data) => {
console.log(data)
},
})
return (
<form onSubmit={form.handleSubmit}>
<Controller
name="name"
control={form.control}
render={({ field, fieldState: { error } }) => (
<div>
<Label>Name</Label>
<Input {...field} />
{error && <p className="text-destructive">{error.message}</p>}
</div>
)}
/>
<Button type="submit">Submit</Button>
</form>
)
}Best Practices
- Export schemas for reuse and testing
- Use TypeScript for type safety
- Keep validation logic in schemas not in components
- Use
watchsparingly for performance - Debounce expensive validations using Zod's
refinewith async - Use
resetafter successful submission - Handle errors gracefully with
onErrorcallback
Examples
See the examples directory for complete working examples.
License
MIT
