npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

@mehdashti/forms

v1.0.0

Published

Enterprise-grade form management with validation, error handling, autosave, and advanced components

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/resolvers

Peer Dependencies:

pnpm add react react-dom @mehdashti/ui @mehdashti/contracts

Quick 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

  1. Export schemas for reuse and testing
  2. Use TypeScript for type safety
  3. Keep validation logic in schemas not in components
  4. Use watch sparingly for performance
  5. Debounce expensive validations using Zod's refine with async
  6. Use reset after successful submission
  7. Handle errors gracefully with onError callback

Examples

See the examples directory for complete working examples.

License

MIT