MiHCM
primitives/@mihcm/ui·v0.21.0·stable

Form

Full-featured form system integrated with TanStack React Form v1. Field wrappers include input, search, textarea, checkbox, switch, select, combobox, radio, radio cards, date, date range, checkbox grid, dropzone, field arrays, subscriptions, effects, and actions.

Foundation

Form uses TanStack Form. Keep MiHCM form documentation, examples, and agent rules on this path; do not introduce React Hook Form for the design-system form layer.

Use cases

  • Complete form experiences — login, registration, contact, settings.
  • Forms that need field-level validation (sync and async).
  • Multi-step wizards with shared form state across steps.
  • Dynamic field arrays (add/remove rows at runtime).

When NOT to use

  • Single uncontrolled input — use Input directly with native form submission.
  • Read-only data display — use Card or Table.
  • Search — use SearchField which handles debounce and clear internally.

Anatomy

Each integrated field (FormInput, FormSelect, etc.) renders a FormItem wrapper containing a FormLabel, the control, an optional FormDescription, and a FormMessage for errors.

┌ FormItem ─────────────────────────────────┐
│  FormLabel (with optional * for required) │
│  [Control: Input / Select / ...]          │
│  FormDescription (muted helper text)      │
│  FormMessage (error with warning icon)    │
└───────────────────────────────────────────┘

Quick start

import { useForm, FormInput } from '@mihcm/ui/Form';
import { Button } from '@mihcm/ui/Button';
 
function MyForm() {
  const form = useForm({
    defaultValues: { email: '' },
    onSubmit: async ({ value }) => console.log(value),
  });
 
  return (
    <form
      onSubmit={(e) => {
        e.preventDefault();
        form.handleSubmit();
      }}
    >
      <FormInput
        form={form}
        name="email"
        label="Email"
        type="email"
        required
        validators={{
          onChange: ({ value }) =>
            !value.includes('@') ? 'Invalid email' : undefined,
        }}
      />
      <Button type="submit">Submit</Button>
    </form>
  );
}

Available fields

ComponentControlValue type
FormInputInputstring
FormTextareaTextareastring
FormCheckboxCheckboxboolean
FormSwitchSwitchboolean
FormSelectSelectstring
FormRadioGroupRadioGroupstring

Utility components

ComponentPurpose
FormFieldArrayManages dynamic add/remove lists via mode="array". Renders each item with a renderField callback.
FormSubscribeReactive state display — wraps form.Subscribe with a selector and render function.
FormListenEffectInvisible field that fires side effects when another field changes (e.g. country → reset city).
FormActionsSubmit + reset button footer. Auto-disables based on canSubmit / isSubmitting state.

FormFieldArray

import { FormFieldArray, FormInput } from '@mihcm/ui/Form';
 
<FormFieldArray
  form={form}
  name="members"
  label="Team members"
  addLabel="Add member"
  renderField={({ index, remove }) => (
    <div className="flex gap-2">
      <FormInput form={form} name={`members[${index}].name`} label="Name" />
      <Button variant="outline" size="sm" onClick={remove}>Remove</Button>
    </div>
  )}
/>

FormSubscribe

import { FormSubscribe } from '@mihcm/ui/Form';
 
<FormSubscribe
  form={form}
  selector={(state) => state.values}
>
  {(values) => <pre>{JSON.stringify(values, null, 2)}</pre>}
</FormSubscribe>

FormListenEffect

import { FormListenEffect } from '@mihcm/ui/Form';
 
<FormListenEffect
  form={form}
  name="country"
  listeners={{
    onChange: ({ value }) => {
      form.setFieldValue('city', '');
    },
  }}
/>

FormActions

import { FormActions } from '@mihcm/ui/Form';
 
<FormActions form={form} submitLabel="Save" showReset />

Composition

  • Form is built on TanStack Form. Define validators in the form config and keep Zod at API, AI descriptor, or host validation boundaries when runtime schemas are required.
  • Each FormField wraps a control (Input, Select, Checkbox) with automatic label, error, and description wiring.
  • FormActions renders Submit + optional Reset buttons aligned to your layout.
  • Nest forms inside Dialog or Sheet for modal editing flows.
  • Input — the underlying text input primitive.
  • Select — the underlying dropdown primitive.
  • Checkbox — the underlying checkbox primitive.
  • Label — form-field label with htmlFor binding.