Skip to main content

Why Formwright

Most teams start forms as local React component code. It works fine for simple forms. As forms grow more complex — conditional fields, async option loading, multi-step flows, cross-form reuse — that code becomes hard to maintain.

The problem with ad-hoc form code

When form behavior lives in components, you end up with:

  • Conditional rendering logic spread across dozens of if statements
  • Validation rules duplicated between server and client
  • No way to reuse form behavior across screens without copying components
  • Custom widgets that are tightly coupled to one form's structure
  • Async option loading implemented differently in every select field

What Formwright does instead

Formwright separates form behavior from rendering:

  • Schema — declaratively describes what the form does: fields, layout, rules, data sources
  • Runtime — evaluates the schema at runtime and derives field state (visible, disabled, required, value)
  • Renderer — turns runtime state into UI; replaceable without touching logic
  • Plugins — extend the engine with custom operators, effects, field types, and data loaders

Your business logic lives in the schema and plugins. Your UI lives in renderers. They don't know about each other.

When to use Formwright

Formwright fits best when you have:

  • Forms with conditional fields driven by other field values
  • Forms that need to work with your existing design system
  • Multiple forms across your app that share behavior or structure
  • A need to configure form behavior without code changes (backend-provided schemas, CMS, registry, etc.)

When plain RHF is enough

If you have a few simple static forms with minimal conditional logic, React Hook Form alone is often enough. Formwright adds value when form behavior complexity justifies a schema layer.

Design priorities

  • Headless runtime — the engine has no UI opinions; you bring your own components
  • Extensibility via plugins — new behavior goes into plugins, not into the engine's core
  • RHF compatibility — built on top of React Hook Form; all RHF features still work
  • TypeScript throughout — schema, runtime state, renderer props, and plugin interfaces are all fully typed