Coming from ngx-formly? The migration guide maps every concept side-by-side and includes a checklist for porting a non-trivial app.

ng-forge generates fully working Angular forms from a single configuration object: validation, conditional logic, and multi-step wizards included. Here's how to set it up.

Quick setup

In an existing Angular 22 workspace:

ng add @ng-forge/dynamic-forms

Pick an adapter when prompted. Works in Nx too; toggle the Nx tab.

Prefer to wire things by hand? Manual setup is below.

1. Choose Your UI Library

Installation

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

Styles

// styles.scss: add a prebuilt Material theme (required)
@import '@angular/material/prebuilt-themes/azure-blue.css';

Setup

import { provideDynamicForm, withLegacyStatusClasses } from '@ng-forge/dynamic-forms';
import { withMaterialFields } from '@ng-forge/dynamic-forms-material';
import { provideAnimations } from '@angular/platform-browser/animations';

export const appConfig: ApplicationConfig = {
  providers: [
    provideAnimations(),
    provideDynamicForm(
      ...withMaterialFields(),
      // Opt into legacy .ng-touched / .ng-invalid CSS classes. Add if your
      // theme or custom CSS targets those classes; safe to omit otherwise.
      withLegacyStatusClasses(),
    ),
  ],
};

Notable Adapter Props

OptionDescription
appearanceSet 'fill' or 'outline' on text inputs, select, and datepicker
colorTheme palette ('primary', 'accent', 'warn') for checkbox, toggle, radio, and slider
labelPositionLabel placement ('before' | 'after') on checkbox, radio, and multi-checkbox
subscriptSizing'fixed' reserves space for hint/error; 'dynamic' collapses when empty
floatLabel'auto', 'always', or 'never' label float behavior
hideRequiredMarkerHide the required asterisk on form fields
hintHelper text below any field


2. Your First Form

Every adapter uses the same FormConfig schema. Import DynamicForm and bind a config object:

@Component({
  imports: [DynamicForm],
  template: `<form [dynamic-form]="config"></form>`,
})
export class ContactComponent {
  config = {
    fields: [
      /* see Config tab below */
    ],
  } as const satisfies FormConfig;
}

Try it out: select a contact method and watch fields appear. Switch to the "Config" tab to see the full schema:

Material Material
Loading live example

Requirements

  • Angular 22: the published packages declare @angular/* peers of ^22.0.0. Signal Forms, which ng-forge builds on, is stable in Angular 22
  • TypeScript 6.0: required by the Angular 22 toolchain

Community & Support

  • Discord: Ask questions, share what you've built, and chat with the community
  • GitHub Issues: Report bugs or request features
  • Contributing: Learn how to contribute to ng-forge

Next Steps