A visual, drag-and-drop form designer built with Angular 20, Angular Material, and Tailwind CSS v4. Create complex enterprise forms in real time — no code required.
- 🖱️ Drag & Drop Canvas — drag fields from the sidebar directly onto the form canvas; reorder within and across rows
- 📐 Multi-Row Layout — add, remove, and reorder rows to create multi-column form layouts
- 🔍 Live Preview — switch between Editor and Preview mode to see the rendered form instantly
- ⚙️ Field-Level Settings — click any field to configure its label, placeholder, required state, type, options, and more in the right panel
- 🧱 8 Field Types — covers the full range of common form inputs
- 🎨 Angular Material + Tailwind — polished UI with a custom Material theme and utility-class styling
- 🔄 Reactive State — built entirely on Angular Signals for fine-grained, performant reactivity
| Field | Icon | Configurable Settings |
|---|---|---|
| Text | text_fields |
Label, Placeholder, Required, Input Type (text / email / number / password) |
| Textarea | notes |
Label, Placeholder, Required |
| Checkbox | check_box |
Label, Required |
| Radio | radio_button_checked |
Label, Required, Options, Orientation (horizontal / vertical) |
| Dropdown | arrow_drop_down_circle |
Label, Required, Options |
| Date Picker | event |
Label, Required, Min Date, Max Date |
| Heading | title |
Heading text, Heading Level (H1–H6), Subheading text, Subheading Level |
| Button | smart_button |
Button text, Cancel text, Show/hide cancel, Alignment (left / right) |
src/
├── app/
│ ├── components/
│ │ ├── form-elements-menu/ # Left sidebar — draggable field palette
│ │ │ └── field-button/ # Individual palette button
│ │ ├── main-canvas/ # Center — editor + preview toggle
│ │ │ ├── form-editor/ # Drag-drop canvas with rows and fields
│ │ │ ├── form-field-component/ # Single field wrapper (drag handle, selection)
│ │ │ ├── form-preview/ # Read-only rendered form view
│ │ │ └── field-preview/ # Single field rendered preview
│ │ └── field-settings/ # Right sidebar — dynamic settings panel
│ │ └── dynamic-setting-field/# Renders a single setting control
│ ├── models/
│ │ ├── field.ts # formField, fieldTypeDefinition, optionItem interfaces
│ │ ├── form.ts # formRow interface
│ │ └── field-definitions.ts # Static metadata & default configs for all 8 types
│ └── services/
│ ├── form.ts # Signal-based form state (rows, selected field, mutations)
│ └── field-types-service.ts # Registry — maps type string → fieldTypeDefinition
┌─────────────────────┐ drag ┌──────────────────────┐
│ FormElementsMenu │ ──────────────► │ FormEditor │
│ (field palette) │ fieldTypeDefn │ (cdkDropList rows) │
└─────────────────────┘ └──────────┬───────────┘
│ addField / moveField
▼
┌──────────────────────┐
│ Form Service │
│ (Signal<formRow[]>) │
└──────┬───────────────┘
read rows │ │ updateField(id, patch)
│ │
┌──────────────┘ └──────────────────┐
▼ ▼
┌──────────────────┐ ┌─────────────────────┐
│ FormPreview │ │ FieldSettings │
│ (live render) │ │ (dynamic controls) │
└──────────────────┘ └─────────────────────┘
| Layer | Technology |
|---|---|
| Framework | Angular 20 (standalone components) |
| UI Components | Angular Material 20 |
| Drag & Drop | Angular CDK DragDropModule |
| Styling | Tailwind CSS v4 + custom SCSS theme |
| State Management | Angular Signals (signal, computed) |
| Language | TypeScript 5.9 |
| Build | Angular CLI / Vite |
| Testing | Karma + Jasmine |
- Node.js ≥ 18
- npm ≥ 9
- Angular CLI ≥ 20
npm install -g @angular/cli# 1. Clone the repository
git clone https://github.com/<your-username>/dynamic-form-builder.git
cd dynamic-form-builder
# 2. Install dependencies
npm install
# 3. Start the dev server
ng serveOpen http://localhost:4200 in your browser.
ng build
# Output: dist/form-designer/ng test┌───────────────────────────────────────────────────────────────────────────────────────────────┐
│ Dynamic Form Builder │
│ Flexible, scalable form creation for modern apps |
├──────────────────────┬─────────────────────────────────────────────────┬──────────────────────┤
│ Field Palette │ Form Canvas │ Field Settings │
│ ───────────── │ ─────────── │ ────────────── │
│ │ │ │
│ [T] Text │ [Editor] [Preview] [+ Add Row] │ Label │
│ [¶] Textarea │ │ ┌──────────────────┐ │
│ [x] Checkbox │ ┌─ Row 1 ─────────────────────────────────────┐ │ │ My Label │ │
│ [o] Radio │ │ ┌────────┐ ┌──────────────┐ │ │ └──────────────────┘ │
│ [v] Dropdown │ │ │ Text │ │ Dropdown │ │ │ │
│ [#] Date │ │ └────────┘ └──────────────┘ │ │ Placeholder │
│ [H] Heading │ └─────────────────────────────────────────────┘ │ ┌──────────────────┐ │
│ [B] Button │ │ │ Enter text... │ │
│ │ ┌─ Row 2 ─────────────────────────────────────┐ │ └──────────────────┘ │
│ Drag any field │ │ │ │ │
│ to the canvas ↓ │ │ Drop fields here... │ │ [ ] Required │
│ │ │ │ │ │
│ │ └─────────────────────────────────────────────┘ │ │
└──────────────────────┴─────────────────────────────────────────────────┴──────────────────────┘
JSON-driven field metadata — every field type is defined as a fieldTypeDefinition object that bundles its default config, settings schema, and rendering component together. Adding a new field type is a single object registration in field-definitions.ts with zero changes elsewhere.
Signal-based state — Form service exposes signal<formRow[]> and a computed selector for the active field. Components stay in sync automatically with no subscriptions to manage.
Dynamic settings panel — FieldSettings reads settingsConfig from the active field's definition and passes each item to DynamicSettingField, which renders the appropriate control (text input, checkbox, select, options editor, date picker). New setting types only need one new @case branch.
CDK drag-drop — uses cdkDropListGroup at the app root so fields can be dragged across row boundaries. The field-selector drop list id distinguishes new-field drops from move operations.
| Command | Description |
|---|---|
ng serve |
Start dev server at localhost:4200 |
ng build |
Production build to dist/ |
ng build --watch --configuration development |
Dev build watcher |
ng test |
Run unit tests with Karma |
MIT © 2024