Your UI config quietly became a programming language. Here's the fix
It always starts the same way. You send a form's fields from the server so the team can change them without a deploy. Reasonable. Then one field has to show only when another field has a certain value. Then a field depends on two others. Then validation has to cross-check them. Then some fields are hidden for some roles. Six months later that payload is full of conditions, dependencies, and branching. It has no types. It has no tests. Nobody can debug it. On a checkout or KYC screen an auditor opens it and cannot tell what it does. You never sat down to design a language. You grew one by accident, and it is running in production right now. Mike Hadlow gave this a name: the Configuration Complexity Clock. Every server-driven UI project sits somewhere on that clock, and it only ticks one way. Blueprint stops it. It is an open-source UI-contract system with a self-hosted visual Builder, and it went public today. Here is how it works, and where it draws the line on purpose. The one rule: describe the screen, not the behavior Blueprint is a UI contract. The contract carries structure, meaning which components go where, with what data bindings, visibility, and access rules. It never carries behavior. Logic stays in typed, testable code. That single boundary is the entire design. A contract is plain JSON: { "version": "1.0", "lib": "tw", "root": { "type": "form", "id": "kyc", "children": [ { "type": "field", "id": "email" }, { "type": "field", "id": "taxId", "visibleWhen": { "field": "country", "equals": "IT" }, "access": { "resource": "customer.taxId", "action": "read" }, "validation": { "required": true, "pattern": "^IT\\d{11}$" } } ] } } Three things matter here. visibleWhen is an object, not a function. It is serializable, so it is inspectable, diffable, and a visual tool can read and write it. You give up some expressiveness for that. On regulated screens, that is exactly the trade you want. access lives inside the field contract. It is not a wrapper bolted on at the call site. There is no code anywhere in the payload. No expression strings. No eval. It renders under a strict CSP. The render pipeline (no eval, ever) The runtime walks the tree and maps each node's type to a real React component from a closed catalog: import { DashBlueprint } from "@dashforge/blueprint"; ; Nothing gets compiled from a string. A node whose type is not in the catalog throws a hard, visible error. It is not a silent injection point. That is what makes it safe to render server-supplied UI on a payment page. The escape hatch: custom nodes A closed catalog would be a cage without a door. The door is custom nodes. You register real, typed React components and reference them by type: ; The catalog covers the 90%. Custom nodes cover the rest, as code, where logic belongs. The payload stays data. This is the line that stops config from ever turning into a language. RBAC and visibility as data (shared front and back) access and visibleWhen are part of the contract, and one policy governs both the API route and the field it protects. On the client this is not decoration: a provider loads that same policy from the server together with the current user's access and wires it into every component, so the rule the server enforces on the route is the rule the UI applies at render. You declare access, you do not hand-write guards. The rest is in the repo. Where Blueprint sits (and where it does not) Builder ──authors──▶ contract (JSON) │ Your backend ──fills with data──▶ envelope (contract + data + state) │ Your frontend (Blueprint runtime) ──renders──▶ real UI Server-driven UI, and not one line of an interpreter reaches the client. Now the honest part. Blueprint is not a compliance product. It does not run your business logic. It does not inventory your third-party scripts. It renders structure and data. It shrinks the surface and governs it. It does not erase the work. Builder: author the contract visually Hand-writing JSON does not scale past a few screens. Builder is a self-hosted visual editor. You drag components, wire visibleWhen and access, validate live, and export the contract. The runtime is open-source. The Builder is self-hosted. You clone the repo and run it. Your contracts never leave your infrastructure. Architecture: https://dashforge-ui.com/blueprint/libraries Try it It is self-hosted. No SaaS. No hosted demo. You clone it and run it locally, which is exactly the point: git clone https://github.com/kensaadi/blueprint # runtime + Builder both live in this repo. run the Builder locally, render contracts with the runtime You can also scaffold a fresh React 19 and TypeScript app on Dashforge: npx dashforge-cli my-app --lib tw Repo (runtime + Builder): https://github.com/kensaadi/blueprint Docs: https://dashforge-ui.com/blueprint One trade-off worth arguing about: serializable conditions instead of functions. Less expressive, but inspectable, diffable, and auditable. On regulated screens that is the whole point. If you have shipped SDUI in production and hit a wall this does not cover, open an issue on the repo. Edge cases are how the catalog earns its keep.
This is a summary aggregated from Dev.to. Read the complete article on the original site:
Read full article at Dev.to