Angular 21+ Signal Forms

Forms are boring.Your code shouldn't be.

Write the config. We handle the rest. Type-safe. Signal-native. Zero boilerplate.

Fromngx-formly?
Monthly Downloads
GitHub Stars
4UI Integrations

Built different.

Not another forms wrapper. Native signal forms integration, real type safety, actual developer experience.

Signal Forms Native

Built directly on Angular Signal Forms. Not a wrapper — real integration with Angular's reactive future.

Actually Type-Safe

as const satisfies — your IDE knows everything. No more guessing.

Any UI Library

One import swap. Same logic everywhere.

Ship Less

Lazy-loaded field components. Load only what you render.

Zoneless Ready

No Zone.js required. OnPush everywhere. Built for where Angular is going.

Escape Hatches

Need custom controls? Same patterns as built-ins. Full power when you need it.

Simple API. Complex forms.

Conditional logic, multi-step wizards, i18n. All declarative.

Conditional Everything

Fields that appear, disappear, or become required based on other values. No imperative code.

{
  key: 'company',
  type: 'input',
  label: 'Company Name',
  logic: [
    {
      type: 'required',
      when: {
        field: 'accountType',
        equals: 'business',
      },
    },
  ],
}

Multi-Step Wizards

Page-level validation, navigation controls, progress tracking. Built-in.

{
  fields: [
    {
      type: 'page',
      key: 'info',
      label: 'Your Info',
      fields: [...],
    },
    {
      type: 'page',
      key: 'payment',
      label: 'Payment',
      fields: [...],
    },
  ],
}

Try to break it.

Real-time validation. No Validators import. Just properties.

Live Demo
validation-config.ts
{
  key: 'email',
  type: 'input',
  label: 'Email',
  required: true,
  email: true,
},
{
  key: 'password',
  type: 'input',
  label: 'Password',
  required: true,
  minLength: 8,
  pattern: /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d).+$/,
  validationMessages: {
    pattern: 'Need uppercase, lowercase & number',
  },
},
{
  key: 'age',
  type: 'input',
  label: 'Age',
  min: 18,
  max: 120,
},
{
  key: 'username',
  type: 'input',
  label: 'Username',
  minLength: 3,
  maxLength: 15,
  pattern: /^[a-z0-9_]+$/,
}

Learn more about validation →

Everything you need, built in.

Common field types ready to use. Need more? Create your own.

inputText, email, password, number
{
  key: 'email',
  type: 'input',
  label: 'Email',
  props: { type: 'email' }
}
textareaMulti-line text
{
  key: 'bio',
  type: 'textarea',
  label: 'Bio',
  props: { rows: 4 }
}
selectDropdown selection
{
  key: 'country',
  type: 'select',
  label: 'Country',
  options: [
    { label: 'United States', value: 'us' },
    { label: 'United Kingdom', value: 'uk' },
  ]
}
radioSingle choice
{
  key: 'plan',
  type: 'radio',
  label: 'Plan',
  options: [
    { label: 'Free', value: 'free' },
    { label: 'Pro', value: 'pro' },
  ]
}
checkboxBoolean toggle
{
  key: 'agree',
  type: 'checkbox',
  label: 'I agree to the terms'
}
multi-checkboxMultiple selections
{
  key: 'interests',
  type: 'multi-checkbox',
  label: 'Interests',
  options: [
    { label: 'Sports', value: 'sports' },
    { label: 'Music', value: 'music' },
  ]
}
toggleSwitch control
{
  key: 'notifications',
  type: 'toggle',
  label: 'Enable notifications'
}
sliderRange input
{
  key: 'volume',
  type: 'slider',
  label: 'Volume',
  props: { min: 0, max: 100 }
}
datepickerDate selection
{
  key: 'birthday',
  type: 'datepicker',
  label: 'Birthday'
}
submitForm submission
{
  key: 'submit',
  type: 'submit',
  label: 'Save'
}
rowHorizontal layout
{
  type: 'row',
  fields: [
    { key: 'firstName', type: 'input', label: 'First' },
    { key: 'lastName', type: 'input', label: 'Last' },
  ]
}
groupNested objects
{
  key: 'address',
  type: 'group',
  fields: [...]
}
pageMulti-step wizards
{
  type: 'page',
  key: 'step1',
  label: 'Info',
  fields: [...]
}
arrayRepeatable fields
{
  key: 'items',
  type: 'array',
  fields: [...]
}
textStatic content
{
  type: 'text',
  label: 'Section Title',
  props: { elementType: 'h3' }
}

See live examples →

Forms don't have to live in code.

Store configs in your database. Fetch via API. Update forms without redeploying.

Database
API Request
JSON
Live Form
form.component.ts
@Component({
  template: `<form [dynamic-form]="config()" />`
})
export class SurveyComponent {
  private http = inject(HttpClient);

  config = toSignal(
    this.http.get<FormConfig>('/api/forms/survey')
  );
}
Form BuildersVisual editors that output JSON
Multi-tenantCustom forms per customer
Dynamic SurveysChange questions on the fly
Hot SwappableForms update when config changes

Your UI library. Our forms.

First-class adapters for everything you're already using.

Or build your own adapter

Stop writing forms.
Start forging them.

npm install @ng-forge/dynamic-forms @ng-forge/material