Forms are boring.Your code shouldn't be.
Write the config. We handle the rest. Type-safe. Signal-native. Zero boilerplate.
Fromngx-formly?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.
{
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_]+$/,
}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' }
}Forms don't have to live in code.
Store configs in your database. Fetch via API. Update forms without redeploying.
@Component({
template: `<form [dynamic-form]="config()" />`
})
export class SurveyComponent {
private http = inject(HttpClient);
config = toSignal(
this.http.get<FormConfig>('/api/forms/survey')
);
}Your UI library. Our forms.
First-class adapters for everything you're already using.
Stop writing forms.
Start forging them.
npm install @ng-forge/dynamic-forms @ng-forge/material