DEV Community

Cla
Cla

Posted on

Why mobx-react-form Is the Best Form Library You're Not Using

If you build forms in React, you've probably tried Formik, React Hook Form, or final-form. They're good. But if you deal with complex, deeply nested, multi-step forms — the kind that make you question your life choices — there's a better tool.

mobx-react-form combines MobX reactivity with a powerful form state engine. Here's why it deserves your attention.


1. Nested Fields Without the Pain

Real forms aren't flat. Take an invoice: you have a customer section, a shipping address, and a variable number of products, each with name, quantity, and price.

MRF handles this natively:

const fields = [
  'customer.name',
  'customer.email',
  'shipping.address',
  'shipping.city',
  'shipping.zip',
  'products',
  'products[].name',
  'products[].quantity',
  'products[].unitPrice',
];
Enter fullscreen mode Exit fullscreen mode

Each path becomes an observable field with its own validation, errors, and dirty tracking. No flattening. No normalization. The values serialize back to exactly the structure your API expects:

{
  "customer": { "name": "...", "email": "..." },
  "shipping": { "address": "...", "city": "...", "zip": "..." },
  "products": [
    { "name": "Widget A", "quantity": 2, "unitPrice": 19.99 }
  ]
}
Enter fullscreen mode Exit fullscreen mode

And with v6.15, errors bubble up automatically:

const form = new MobxReactForm({ fields }, {
  options: { bubbleUpErrorMessages: true }
});

// When products[0].product is empty, this just works:
{form.error && <ErrorBanner message={form.error} />}
Enter fullscreen mode Exit fullscreen mode

2. Validation Plugins — Bring Your Own

MRF doesn't lock you into one validator. It supports 6 validation plugins out of the box:

Plugin Package Style
DVR validatorjs Declarative rules (`'required
VJF Custom functions {% raw %}(value) => [isValid, message]
ZOD zod Schema-based
YUP yup Schema-based
JOI joi Schema-based
SVK ajv Schema-based

Use DVR for simple fields and ZOD for complex cross-field validation — in the same form:

plugins() {
  return {
    dvr: dvr({ package: validatorjs }),
    zod: zodPlugin({ package: z, schema: invoiceSchema }),
  };
}
Enter fullscreen mode Exit fullscreen mode

Define a Zod schema for your entire form and get both client-side validation and TypeScript types from one source:

const invoiceSchema = z.object({
  customer: z.object({
    name: z.string().min(2),
    email: z.string().email(),
  }),
  products: z.array(z.object({
    name: z.string().min(1),
    quantity: z.number().min(1),
    unitPrice: z.number().positive(),
  })).min(1),
});
Enter fullscreen mode Exit fullscreen mode

3. Full TypeScript Generics (v6.13)

If you're on TypeScript, MRF now offers end-to-end type safety:

interface InvoiceForm {
  customer: { name: string; email: string };
  shipping: { address: string; city: string; zip: string };
  products: Array<{ name: string; quantity: number; unitPrice: number }>;
}

const form = new MobxReactForm<InvoiceForm>({ fields });

form.$('products[0]').$('name'); // Field<string> — fully typed
form.values();                        // InvoiceForm — fully typed
form.$('customer').$('name').set('Acme Corp'); // type-safe setter
Enter fullscreen mode Exit fullscreen mode

Plus PathsOf<T> for autocomplete:

form.$('ship') // → editor suggests: 'shipping.address' | 'shipping.city' | 'shipping.zip'
Enter fullscreen mode Exit fullscreen mode

No more guessing field paths. No more runtime errors from typos.


4. Dynamic & Array Fields

Need to add products dynamically? Remove a shipping address from a list? Built in.

// Add a product
form.$('products').add({
  product: 'New Product',
  quantity: 1,
  unitPrice: 0,
});

// Remove the second product
form.$('products').del(1);

// Reorder (new in v6.14 with ArrayMap)
form.$('products').move(0, 2);
Enter fullscreen mode Exit fullscreen mode

Arrays, nested arrays, dynamic field creation — MRF handles all of it with MobX reactivity. Each item gets its own validation lifecycle, dirty tracking, and error state.


5. Composer — Multi-Form Orchestration

Have independent forms that need to submit together? The composer() function coordinates validation across forms:

const checkout = composer({ billing, shipping, payment });

checkout.validate().then(({ valid, errors, values }) => {
  if (valid) {
    submitOrder({
      ...values.billing,
      shipping: values.shipping,
      payment: values.payment,
    });
  } else {
    // errors is keyed by form name: { billing: {...}, shipping: {...}, payment: {...} }
    showValidationSummary(errors);
  }
});
Enter fullscreen mode Exit fullscreen mode

Perfect for checkout flows, multi-tab dashboards, and wizard-style UIs where each step is an independent form.


6. UI Library Agnostic

MRF works with every major UI library — and doesn't force you to pick one:

  • Material UI
  • Ant Design
  • React Aria
  • Headless UI
  • React Widgets
  • React-Select
  • Plain HTML

Each gets a thin binding layer. The form logic stays the same whether you render with MUI TextFields or raw <input> elements.

// Works the same regardless of UI library
<form onSubmit={form.onSubmit}>
  <Input field={form.$('customer.name')} />
  <Input field={form.$('customer.email')} />
  <button type="submit">Save</button>
</form>
Enter fullscreen mode Exit fullscreen mode

Swap the Input component from MaterialTextField to AntdInput to SimpleInput — the form state doesn't care.


7. Built for Production, Maintained for Years

MRF has been powering production forms for 8+ years. It has 1.1k+ GitHub stars, and the latest releases (June 2026) brought the most requested features:

  • v6.15 — Error bubbling in nested forms
  • v6.14 — ArrayMap with predictable ordering and drag-and-drop support
  • v6.13 — Full TypeScript generics, strict null checks, path autocomplete
  • v6.12 — Custom validation type inference, YUP .ref() support
  • v6.11 — Full null values support

Each release was driven by real production needs, not hypothetical use cases.


The Bottom Line

Scenario MRF Alternatives
Simple login form ✅ Overkill but works ✅ Great fit
Deeply nested fields (invoice, order, profile) ✅ Native support ❌ Requires flattening
Dynamic arrays (add/remove/reorder) ✅ Built-in ❌ Manual state mgmt
Multi-form validation (checkout, wizard) ✅ Composer ❌ Custom orchestration
Validation plugins (DVR/ZOD/YUP/JOI) ✅ 7 plugins ❌ Usually 1-2
TypeScript generics + path autocomplete ✅ v6.13+ ❌ Limited
Error bubbling in nested forms ✅ v6.15+ ❌ Manual recursion
UI library flexibility ✅ Any library ✅ Any library

If you build forms beyond a login page — invoices, checkouts, multi-step registrations, admin panels with dynamic sections — give MRF a try. It handles the complexity so you don't have to.

npm install --save mobx-react-form
Enter fullscreen mode Exit fullscreen mode

Try the live demo | GitHub | Docs

Top comments (0)