DEV Community

Cover image for Schema-Driven, Framework-Agnostic Forms: Building a Runtime Engine for React, Vue, Angular & Vanilla JS
yanggmtl
yanggmtl

Posted on

Schema-Driven, Framework-Agnostic Forms: Building a Runtime Engine for React, Vue, Angular & Vanilla JS

In this article, I explain how to build dynamic, schema-driven forms that work across React, Vue, Angular, and Vanilla JS using a registry-driven runtime engine. The forms fully decouple data, UI, and logic, and support conditional logic, dynamic validation, and plugin extensibility—all without hardcoding components.
You can try it out here: GitHub Repository | Install via npm

In most form systems, even schema-driven ones, logic is still tightly coupled to structure or UI.

The goal of Formitiva is to go further:

Fully decouple data, presentation, and behavior

The Problem with Traditional Separation

Typical systems separate:

  • data (JSON schema)
  • UI (components)

But logic often remains:

  • embedded in schema
  • hardcoded in components
  • tied to specific frameworks

This creates hidden coupling.

The Key Idea: Reference, Don’t Embed

Instead of embedding logic:

{
  "field": "email",
  "validatorHandlerName": "emailValidator"
}
Enter fullscreen mode Exit fullscreen mode

The schema only references behavior.

It does not define:

  • how validation works
  • how UI renders
  • how logic executes

The Registry System

All implementations live in a registry:

  • component registry
  • validation registry
  • logic/workflow registry

The registry acts as a lookup system:

string reference → actual implementation
Enter fullscreen mode Exit fullscreen mode

The Runtime Engine

The runtime engine is responsible for:

  • Parsing schema
  • Resolving references via registry
  • Executing logic (validation, conditions)
  • Delegating rendering

This transforms the system into:

Schema → References → Registry → Runtime Execution
Enter fullscreen mode Exit fullscreen mode

Renderer Abstraction (Framework-Agnostic Design)

The runtime does not render UI directly.

Instead, it delegates to renderer adapters:

  • React
  • Angular
  • Vue
  • Vanilla JS

Each adapter:

  • receives abstract UI definitions
  • maps them to framework-specific components

The Resulting Architecture

You get a layered system:

  • Schema → declarative intent
  • Registry → implementation mapping
  • Runtime → execution engine
  • Renderer → UI layer

Extensibility Through Plugins

Because everything is resolved via registry:

  • new components can be added
  • new validators can be injected
  • new behaviors can be introduced

Without modifying core logic.

Why This Matters

This architecture enables:

  • true separation of concerns
  • cross-framework portability
  • backend-driven UI
  • low-code possibilities
  • long-term scalability

Final Thought

Decoupling isn’t just about separating files or layers.

It’s about removing direct knowledge between parts of the system.

The registry pattern achieves that by introducing controlled indirection at runtime.

That’s what makes Formitiva fundamentally different.

Top comments (0)