Creating dynamic forms that users can design at runtime—dragging fields, configuring validation, and previewing behaviour—gives product teams huge flexibility. Such a system lets business users create surveys, work orders, onboarding flows, or admin screens without developer changes. In this article you will learn a practical, production-ready approach to build a fully dynamic form builder using Angular (frontend), ASP.NET Core (backend), and SQL Server (metadata store). The guide covers architecture, data model, UX patterns, example code, and operational best practices.
Target audience: beginner → expert web developers (especially full-stack .NET + Angular engineers).
What is a Dynamic Form Builder?
A dynamic form builder provides:
A drag-and-drop canvas to add fields (text, number, date, select, radio, checkbox, file, custom components).
A field property editor to set label, placeholder, default value, required, validation rules, help text, conditional visibility.
A validation rule engine (regex, min/max, cross-field rules).
A preview mode to test behaviour before publishing.
A runtime renderer that converts saved form definitions into working forms.
APIs to save form definitions and collect submissions.
High-level architecture
User (Designer) <-> Angular Form Builder UI <-> ASP.NET Core API <-> SQL Server (Form + Field metadata)
|
v
Submission Store / Blob (files)
Key components
Angular Builder: drag/drop canvas, property panel, preview.
Form Renderer (Angular): runtime component that builds reactive forms from metadata.
Backend API (ASP.NET Core): store/retrieve form definitions, validation evaluation, submission endpoint.
SQL Server: canonical store for forms, fields, rules, versions.
Optional: Redis cache for published forms, blob storage for file uploads, message queue for submission processing.
Flowchart
Designer opens Builder
↓
Drag & drop fields on canvas
↓
Configure field properties + validation
↓
Preview form (client-side)
↓
Publish → API saves form JSON + version
↓
Users submit runtime form → API validates & stores submission
Workflow (smaller header)
Design -> Preview -> Publish -> Render at runtime -> Submit -> Validate -> Store
ER diagram (smaller header)
+-------------------+ +----------------------+ +----------------------+
| Forms | 1 - *| Fields | 1 - *| ValidationRules |
+-------------------+ +----------------------+ +----------------------+
| FormId (PK) | | FieldId (PK) | | RuleId (PK) |
| Name | | FormId (FK) | | FieldId (FK) |
| Version | | Type (text, select) | | RuleType (regex,...) |
| IsPublished | | Label | | Expression |
| CreatedOn | | Key (jsonKey) | | ErrorMessage |
+-------------------+ | Options (JSON) | +----------------------+
| Order |
| Conditional (JSON) |
+----------------------+
+----------------------+
| Submissions |
+----------------------+
| SubmissionId (PK) |
| FormId (FK) |
| Data (JSON) |
| CreatedOn |
+----------------------+
Architecture diagram (Visio-style, smaller header)
[ Angular Builder ] <---HTTP---> [ ASP.NET Core API ] <---EF Core---> [ SQL Server ]
| (Drag/Drop) | |
| (Preview) | |
v v v
[ Angular Renderer ] [ Validation Engine ] [ Blob Storage (files) ]
| (runtime form) | |
v v v
[ User Submits ] --------------------> [ Submission API ] --------------> [ Submissions Table ]
Sequence diagram (smaller header)
Designer -> AngularBuilder: open form
AngularBuilder -> User: shows canvas
Designer -> AngularBuilder: add field, set props
AngularBuilder -> PreviewComponent: render preview
Designer -> AngularBuilder: Publish
AngularBuilder -> API: POST /forms
API -> SQLServer: save form + version
User -> AngularRenderer: GET /forms/{id}
AngularRenderer -> API: GET form definition
API -> SQLServer: fetch form JSON
AngularRenderer -> User: render form
User -> AngularRenderer: submit form
AngularRenderer -> API: POST /forms/{id}/submit
API -> ValidationEngine: validate submission
ValidationEngine -> API: returns result
API -> SQLServer: store submission
API -> AngularRenderer: return success/fail
Data model and JSON format
Store a form as JSON in DB (plus normalized fields if needed). Example simplified JSON:
{
"formId": "guid",
"name": "Work Order",
"version": 3,
"fields": [
{
"id": "f1",
"key": "customerName",
"type": "text",
"label": "Customer Name",
"placeholder": "Enter full name",
"required": true,
"order": 1
},
{
"id": "f2",
"key": "priority",
"type": "select",
"label": "Priority",
"options": ["Low","Medium","High"],
"required": true,
"order": 2
},
{
"id": "f3",
"key": "dueDate",
"type": "date",
"label": "Due Date",
"required": false,
"conditional": {
"showIf": { "priority": "High" }
}
}
]
}
Keep ValidationRules either embedded in field JSON or as a separate table for complex rules and versioning.
Frontend: Angular builder and renderer
Libraries & concepts
Use Angular CDK DragDrop for drag-drop canvas.
Use Reactive Forms (
FormGroup,FormControl) for runtime form building.Use a component library (Material, PrimeNG) for neat UI.
Keep a Field Registry mapping
type -> componentso you can add custom field types later.

Join the conversation! Your thoughts help the community grow.