Skip to content

What it is

driven-form is a headless React form engine. Your backend sends a JSON schema describing the form; your frontend renders it with components you wrote once.

The library owns logic and state. Which fields show, which are required, which are disabled, what values are derived from other values, what’s valid, and how array rows behave.

You own every pixel. The engine ships no UI at all. It hands your component a value, an error, and a change handler, and gets out of the way.

That’s the whole contract. A TextInput you write on day one never needs touching again, no matter how the form changes.

Conditions are expressions inside the schema:

%fieldName% truthy check
%country% == 'IN' equals
%age% > 18 && %hasConsent% boolean logic
%price% * %quantity% calculated value
sum(%rows%, 'amount') across repeater rows

They drive show, required, disabled and calculateValue on any field, and they’re evaluated by a sandboxed AST interpreter — never eval().

  • Conditional fields without a single useState in your form code
  • Validation, sync and async, with per-rule validateOn control
  • Calculated values that settle a whole dependency chain in one render pass
  • Repeaters — array fields with add/remove/move, scoped per-row validation, and render isolation between rows
  • A render-count contract: a field re-renders only when its own value, its own error, or a declared dependency changes. Enforced by tests, not by hope.

Reach for driven-form when forms change more often than your release cycle, when different tenants or products need different fields, or when someone who isn’t a frontend engineer — a PM, a CMS, an LLM — should be able to change a form.

It’s less useful for a single hardcoded login form. Two fields and no conditions don’t need an engine. And if your forms live in your code and change when your code changes, a library like React Hook Form will serve you better — how it compares goes through that honestly.

Pre-1.0. The core engine, expression language, registry, validation, lazy fields and repeaters are implemented and covered by 271 tests. The API may still shift before a 1.0 tag.