Skip to content

Repository files navigation

Formwright — Drag-and-Drop Form Builder Platform

An internal no-code/low-code platform for building nested, conditional forms without code. Every form compiles to a single normalized tree schema that a standalone renderer package and an API mapper share as one source of truth. Built to the PRD.

Architecture

A pnpm monorepo with a clean separation between the framework-agnostic core, the presentational UI kit, the React renderer, the API, and the builder app.

packages/
  form-schema          @internal/form-schema         Normalized tree schema types (nodes, conditions, compute, mapping)
  form-renderer-core   @internal/form-renderer-core   Framework-agnostic engine: recursive walker, validation,
                                                      sandboxed expression evaluator, cycle detection, schema health
  ui                   @internal/ui                   Standalone presentational component library + design tokens
  form-renderer        @internal/form-renderer         React <FormRenderer> consuming core + ui (componentMap override)
apps/
  backend              @internal/backend               Hono + TypeScript API (forms CRUD, draft/publish/versioning, mappings)
  builder              @internal/builder               Vite + React builder UI (palette, canvas, inspector, preview, mapper)

The three modules (PRD §5)

  1. Form Builder — drag fields from a palette onto a canvas, nest them into option branches or containers, configure validation / conditional visibility / computed formulas, preview live, and publish immutable versions.
  2. Form Renderer (@internal/form-renderer) — <FormRenderer schema initialValues onSubmit /> plus validateForm, flattenSchema, getVisibleFields. Consuming teams theme it via a componentMap override or use the shipped defaults.
  3. API Mapper — map each flattened fieldId to a downstream API key (dot-notation, transforms, if-absent policy), preview the payload, and publish a versioned mapping pinned to a form version.

Core innovation — normalized tree + recursive resolution

Nodes are stored flat in nodes keyed by id; structure is expressed via parentId + children / childrenByOptionValue. A single recursive engine (resolve in form-renderer-core) resolves computed values in dependency order, evaluates per-field validation state, and walks visibility — supporting arbitrary depth, cross-branch visibleWhen rules, validation-state–driven visibility (isValid / isInvalid / isAnswered), and computed fields (including sum() aggregation across repeatable rows) without per-level special-casing.

Getting started

pnpm install
pnpm build          # build the four packages (schema, core, ui, renderer)

# run both apps (in separate terminals, or `pnpm dev` for both)
pnpm dev:backend    # Hono API on http://localhost:8787
pnpm dev:builder    # Builder UI on http://localhost:5173 (proxies /api → 8787)

Open http://localhost:5173 and sign in. A seeded Business Onboarding (KYC) form demonstrates nesting, a cross-branch rule (GSTIN shows only for India), validation-state visibility (document upload appears once the registration number is valid), a computed full name, and a repeatable line-items group with a computed grand total.

Data & auth

State lives in a SQLite database at apps/backend/data/app.db (created and migrated on first boot via node:sqlite; requires Node ≥ 22.5). The API requires authentication — every route except /api/health and /api/auth/login expects an Authorization: Bearer <token> session. Resources are owned by a team; members of that team can edit and publish, while everyone else has cross-team read-only access (PRD §9).

Seeded demo accounts (all use the password password):

Email Teams
ada@internal.dev Platform
grace@internal.dev Growth
alan@internal.dev Platform + Growth

Scripts

Command Description
pnpm build Build all publishable packages
pnpm typecheck Typecheck every workspace project
pnpm dev Run backend + builder together
pnpm --filter @internal/form-renderer-core test Run the engine test suite

Status

v1 implements the full happy path across all three modules. v2 (in progress) adds the foundation: a migrated SQLite datastore replacing the JSON file, session-based authentication, and enforced per-team permissions (edit/publish gated to owning-team members; cross-team read-only). Remaining known simplifications: per-row validation-state (used inside repeatable groups) is evaluated at global scope, file uploads store metadata rather than binary content, and dev login authenticates by email + shared password rather than a real identity provider.

About

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages