Mosmox UI
Current version
GitHubnpmjs.com
Forms & Inputs

Form

Coordinate field values, validation, submission, and reset behavior.

The Form component is an orchestration layer for the form controls in Mosmox UI. It keeps values together, connects fields to the same validation lifecycle, exposes consistent errors, and provides both declarative and imperative submission APIs.

Installation

import { Form, Input, Button } from '@mosmox/ui';

Basic usage

Form.Field uses a render prop so it can wrap any existing control without replacing that control's API.

<Form
  initialValues={{ email: '' }}
  onSubmit={(values) => console.log(values)}
>
  <Form.Field
    name="email"
    rules={[
      { required: true },
      { pattern: /^[^\s@]+@[^\s@]+\.[^\s@]+$/, message: 'Enter a valid email.' },
    ]}
  >
    {(field) => (
      <Input
        label="Email"
        type="email"
        value={String(field.value ?? '')}
        error={field.error}
        required={field.required}
        name={field.name}
        onChange={field.onChange}
        onBlur={field.onBlur}
      />
    )}
  </Form.Field>
  <Form.Actions>
    <Button type="submit">Save</Button>
  </Form.Actions>
</Form>

The form prevents the browser's default navigation, validates registered fields, and calls onSubmit only when the result is valid. onInvalid receives a map of field names to messages.

Working with existing controls

The field adapter accepts native events as well as direct values. This makes it compatible with the existing form components:

<Form.Field name="name">{(field) => <Input {...field.controlProps} label="Name" />}</Form.Field>
<Form.Field name="team">{(field) => (
  <Select
    label="Team"
    options={[{ value: 'design', label: 'Design' }]}
    value={typeof field.value === 'string' ? field.value : undefined}
    onChange={(value) => field.onChange(value)}
    error={field.error}
  />
)}</Form.Field>
<Form.Field name="enabled">{(field) => (
  <Switch checked={Boolean(field.value)} onChange={field.onChange} label="Enabled" />
)}</Form.Field>
<Form.Field name="terms">{(field) => (
  <Checkbox checked={Boolean(field.value)} onChange={field.onChange} label="Accept terms" error={field.error} />
)}</Form.Field>

The same pattern works with Textarea, InputNumber, InputPassword, InputOTP, InputMask, InputCurrency, ColorPicker, Radio, and other controls. Controls with a value callback can pass the value to field.onChange directly.

Validation

Rules run in declaration order. A rule can contain:

PropertyTypeDescription
requiredbooleanRejects empty values.
messagestringCustom message for that rule.
minLengthnumberMinimum string length.
maxLengthnumberMaximum string length.
patternRegExpRegular expression constraint.
validator(value, values) => string | undefined | Promise<string | undefined>Custom synchronous or asynchronous validation.

Choose when validation runs with validateOn: submit (default), change, blur, or change-and-blur.

<Form validateOn="change-and-blur">
  <Form.Field
    name="username"
    rules={[
      { required: true },
      { minLength: 3 },
      { validator: async (value) => isAvailable(String(value)) ? undefined : 'Name is already taken.' },
    ]}
  >
    {(field) => <Input {...field.controlProps} label="Username" />}
  </Form.Field>
</Form>

Values, reset, and submit

Use initialValues for the starting snapshot. Pass values when the parent owns the complete value object. onValuesChange reports the latest object and the field that changed.

const formRef = useRef<FormInstance>(null);

<Form ref={formRef} initialValues={{ query: '' }}>
  {/* fields */}
</Form>

formRef.current?.setFieldValue('query', 'documentation');
const valid = await formRef.current?.validate();
await formRef.current?.submit();
formRef.current?.reset();

The FormInstance also provides getValues, getFieldValue, getFieldError, setValues, and setFieldError for integration with wizards, autosave, and server-side errors.

Layout and configuration

Use layout="vertical" for the default stacked layout, horizontal for label/control rows, or inline for compact filters. size accepts sm, default, and lg. Set disabled on the form to disable the form's visual container and pass it to controls as needed.

<ConfigProvider components={{ form: { layout: 'horizontal', size: 'sm', validateOn: 'blur' } }}>
  <Form>{/* all forms in this subtree use these defaults */}</Form>
</ConfigProvider>

Individual props always override ConfigProvider defaults. The component uses the shared theme tokens and existing control error states, so it can be introduced incrementally into forms that already use Mosmox UI fields.

API reference

Form props

PropTypeDefault
initialValuesRecord<string, unknown>{}
valuesRecord<string, unknown>—
layoutvertical | horizontal | inlinevertical
sizesm | default | lgdefault
validateOnchange | blur | submit | change-and-blursubmit
disabledbooleanfalse
onValuesChange(values, changedField) => void—
onSubmit(values, event) => void | Promise<void>—
onInvalid(errors) => void—
onReset(values) => void—

Subcomponents

  • Form.Field — registers a named value and supplies value, error, touched, validating, required, onChange, onBlur, and controlProps.
  • Form.Actions — aligns submit/reset actions with start, center, end, or between.