Getting Started with Angular Formly

Learn how to build dynamic forms with Angular Formly from scratch

⏱️ 30 minutes read
📅 Last updated: June 2025
🎯 Beginner Level

1. Introduction to Angular Formly

Angular Formly is a powerful library that allows you to build dynamic forms in Angular using JSON configuration. Instead of writing HTML templates for each form, you can define your forms using JavaScript objects, making form creation faster and more maintainable.

Why Use Angular Formly?

Traditional Angular forms require you to write HTML templates, component logic, and validation rules separately. This approach can become cumbersome when dealing with complex forms or when you need to generate forms dynamically. Angular Formly solves these problems by providing:

  • Configuration-driven approach: Define forms using JSON objects
  • Automatic form generation: No need to write HTML templates
  • Built-in validation: Easy to add and customize validation rules
  • Extensibility: Create custom field types and validators
  • Type safety: Full TypeScript support
💡 Pro Tip

Angular Formly is perfect for applications that need to generate forms dynamically, such as survey builders, admin panels, or configuration interfaces.

2. Installation and Setup

Let's start by installing Angular Formly in your Angular project. We'll use the Bootstrap UI theme for this tutorial, but you can choose other themes based on your needs.

Step 1: Install Angular Formly

Install the core Formly library and the Bootstrap theme:

Terminal
npm install @ngx-formly/core @ngx-formly/bootstrap

Step 2: Install Bootstrap (Optional)

If you don't have Bootstrap installed, add it to your project:

Terminal
npm install bootstrap

Step 3: Import Formly Modules

Add the Formly modules to your app module:

app.module.ts
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { ReactiveFormsModule } from '@angular/forms';
import { FormlyModule } from '@ngx-formly/core';
import { FormlyBootstrapModule } from '@ngx-formly/bootstrap';

import { AppComponent } from './app.component';

@NgModule({
  declarations: [AppComponent],
  imports: [
    BrowserModule,
    ReactiveFormsModule,
    FormlyModule.forRoot(),
    FormlyBootstrapModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

Step 4: Add Bootstrap Styles

Include Bootstrap CSS in your angular.json file or styles.css:

styles.css
@import '~bootstrap/dist/css/bootstrap.min.css';
📋 Note

Angular Formly supports multiple UI themes including Material, Bootstrap, Ionic, and more. You can also create custom themes.

3. Creating Your First Form

Now let's create a simple contact form to understand how Angular Formly works. We'll create a form with name, email, and message fields.

Step 1: Set Up the Component

First, let's set up our component with the necessary imports and basic structure:

contact-form.component.ts
import { Component } from '@angular/core';
import { FormGroup } from '@angular/forms';
import { FormlyFieldConfig } from '@ngx-formly/core';

@Component({
  selector: 'app-contact-form',
  templateUrl: './contact-form.component.html'
})
export class ContactFormComponent {
  form = new FormGroup({});
  model = {};
  fields: FormlyFieldConfig[] = [];

  onSubmit() {
    if (this.form.valid) {
      console.log('Form Data:', this.model);
    }
  }
}

Step 2: Define Form Fields

Now let's define our form fields using the Formly configuration:

contact-form.component.ts (updated)
export class ContactFormComponent {
  form = new FormGroup({});
  model = {};
  fields: FormlyFieldConfig[] = [
    {
      key: 'name',
      type: 'input',
      templateOptions: {
        label: 'Full Name',
        placeholder: 'Enter your full name',
        required: true
      }
    },
    {
      key: 'email',
      type: 'input',
      templateOptions: {
        label: 'Email Address',
        placeholder: 'Enter your email',
        type: 'email',
        required: true
      }
    },
    {
      key: 'message',
      type: 'textarea',
      templateOptions: {
        label: 'Message',
        placeholder: 'Enter your message',
        rows: 4,
        required: true
      }
    }
  ];

  onSubmit() {
    if (this.form.valid) {
      console.log('Form Data:', this.model);
    }
  }
}

Step 3: Create the Template

Create the HTML template for your form:

contact-form.component.html
<div class="container mt-4">
  <h2>Contact Form</h2>
  <form [formGroup]="form" (ngSubmit)="onSubmit()">
    <formly-form 
      [form]="form" 
      [fields]="fields" 
      [model]="model">
    </formly-form>
    
    <button 
      type="submit" 
      class="btn btn-primary"
      [disabled]="!form.valid">
      Submit
    </button>
  </form>
  
  <!-- Debug Information -->
  <div class="mt-4">
    <h5>Form Data:</h5>
    <pre>{{ model | json }}</pre>
  </div>
</div>
🎯 Understanding the Structure

The formly-form component takes three main inputs: form (FormGroup), fields (configuration), and model (data object). This is all you need to render a complete form!

4. Understanding Field Types

Angular Formly provides many built-in field types. Let's explore the most commonly used ones and how to configure them.

Common Field Types

Here are the most frequently used field types with examples:

field-types.example.ts
fields: FormlyFieldConfig[] = [
  // Text Input
  {
    key: 'firstName',
    type: 'input',
    templateOptions: {
      label: 'First Name',
      placeholder: 'Enter first name',
      required: true
    }
  },
  
  // Email Input
  {
    key: 'email',
    type: 'input',
    templateOptions: {
      label: 'Email',
      type: 'email',
      required: true
    }
  },
  
  // Password Input
  {
    key: 'password',
    type: 'input',
    templateOptions: {
      label: 'Password',
      type: 'password',
      required: true,
      minLength: 8
    }
  },
  
  // Textarea
  {
    key: 'description',
    type: 'textarea',
    templateOptions: {
      label: 'Description',
      rows: 3,
      placeholder: 'Enter description'
    }
  },
  
  // Select Dropdown
  {
    key: 'country',
    type: 'select',
    templateOptions: {
      label: 'Country',
      options: [
        { value: 'us', label: 'United States' },
        { value: 'uk', label: 'United Kingdom' },
        { value: 'ca', label: 'Canada' }
      ],
      required: true
    }
  },
  
  // Checkbox
  {
    key: 'terms',
    type: 'checkbox',
    templateOptions: {
      label: 'I agree to the terms and conditions',
      required: true
    }
  },
  
  // Radio Buttons
  {
    key: 'gender',
    type: 'radio',
    templateOptions: {
      label: 'Gender',
      options: [
        { value: 'male', label: 'Male' },
        { value: 'female', label: 'Female' },
        { value: 'other', label: 'Other' }
      ]
    }
  }
];

Advanced Field Configuration

You can also use advanced features like conditional fields and expressions:

advanced-fields.example.ts
fields: FormlyFieldConfig[] = [
  {
    key: 'hasAccount',
    type: 'checkbox',
    templateOptions: {
      label: 'I already have an account'
    }
  },
  {
    key: 'username',
    type: 'input',
    templateOptions: {
      label: 'Username',
      required: true
    },
    // Show only if hasAccount is true
    hideExpression: '!model.hasAccount'
  },
  {
    key: 'email',
    type: 'input',
    templateOptions: {
      label: 'Email',
      type: 'email',
      required: true
    },
    // Show only if hasAccount is false
    hideExpression: 'model.hasAccount'
  }
];
⚠️ Important

Always use the key property to specify the field name. This is how Formly maps the field to your model object.

5. Adding Validation

Angular Formly provides powerful validation features. You can use built-in validators or create custom ones.

Built-in Validators

Formly comes with common validators that you can use directly in your field configuration:

validation.example.ts
fields: FormlyFieldConfig[] = [
  {
    key: 'email',
    type: 'input',
    templateOptions: {
      label: 'Email',
      type: 'email',
      required: true
    },
    validators: {
      validation: ['email']
    }
  },
  {
    key: 'password',
    type: 'input',
    templateOptions: {
      label: 'Password',
      type: 'password',
      required: true,
      minLength: 8,
      maxLength: 20
    }
  },
  {
    key: 'confirmPassword',
    type: 'input',
    templateOptions: {
      label: 'Confirm Password',
      type: 'password',
      required: true
    },
    validators: {
      fieldMatch: {
        expression: (control) => {
          const value = control.value;
          return control.parent &&
                 control.parent.get('password') &&
                 control.parent.get('password').value === value;
        },
        message: 'Passwords must match'
      }
    }
  }
];

Custom Validators

You can create custom validators for specific business logic:

custom-validator.example.ts
import { FormlyFieldConfig } from '@ngx-formly/core';

        // Custom validator function
function usernameValidator(control: any) {
  const value = control.value;
  if (!value) return null;
  
  // Username should not contain spaces
  if (value.includes(' ')) {
    return { 'username': { message: 'Username cannot contain spaces' } };
  }
  
  // Username should be at least 3 characters
  if (value.length < 3) {
    return { 'username': { message: 'Username must be at least 3 characters' } };
  }
  
  return null;
}

fields: FormlyFieldConfig[] = [
  {
    key: 'username',
    type: 'input',
    templateOptions: {
      label: 'Username',
      required: true
    },
    validators: {
      validation: [usernameValidator]
    }
  }
];

Async Validators

For server-side validation, you can use async validators:

async-validator.example.ts
import { of, timer } from 'rxjs';
import { map, switchMap } from 'rxjs/operators';

function asyncEmailValidator(control: any) {
  if (!control.value) {
    return of(null);
  }

  // Simulate API call with timer
  return timer(1000).pipe(
    switchMap(() => {
      // Simulate checking if email exists
      const emailExists = control.value === '[email protected]';
      return of(emailExists ? { 'emailTaken': true } : null);
    })
  );
}

fields: FormlyFieldConfig[] = [
  {
    key: 'email',
    type: 'input',
    templateOptions: {
      label: 'Email',
      type: 'email',
      required: true
    },
    asyncValidators: {
      emailAvailable: {
        expression: asyncEmailValidator,
        message: 'This email is already taken'
      }
    }
  }
];
✨ Validation Messages

Formly automatically displays validation messages. You can customize them by providing a message property in your validator configuration.

6. Next Steps

Congratulations! You've learned the basics of Angular Formly. Here's what you can explore next to become more proficient:

Advanced Topics to Explore

  • Custom Field Types: Create your own field components for specific use cases
  • Form Templates: Build reusable form templates for common patterns
  • Dynamic Forms: Generate forms based on API responses or user interactions
  • Styling and Theming: Customize the appearance of your forms
  • Form Wizards: Create multi-step forms with navigation
  • Integration: Connect Formly with state management libraries like NgRx

Complete Example

Here's a complete working example that combines everything we've learned:

complete-form.component.ts
import { Component } from '@angular/core';
import { FormGroup } from '@angular/forms';
import { FormlyFieldConfig } from '@ngx-formly/core';

@Component({
  selector: 'app-complete-form',
  template: `
    <div class="container mt-4">
      <h2>User Registration Form</h2>
      <form [formGroup]="form" (ngSubmit)="onSubmit()">
        <formly-form [form]="form" [fields]="fields" [model]="model"></formly-form>
        <button type="submit" class="btn btn-primary" [disabled]="!form.valid">
          Register
        </button>
      </form>
    </div>
  `
})
export class CompleteFormComponent {
  form = new FormGroup({});
  model = {};
  
  fields: FormlyFieldConfig[] = [
    {
      fieldGroupClassName: 'row',
      fieldGroup: [
        {
          className: 'col-6',
          key: 'firstName',
          type: 'input',
          templateOptions: {
            label: 'First Name',
            required: true
          }
        },
        {
          className: 'col-6',
          key: 'lastName',
          type: 'input',
          templateOptions: {
            label: 'Last Name',
            required: true
          }
        }
      ]
    },
    {
      key: 'email',
      type: 'input',
      templateOptions: {
        label: 'Email Address',
        type: 'email',
        required: true
      }
    },
    {
      key: 'password',
      type: 'input',
      templateOptions: {
        label: 'Password',
        type: 'password',
        required: true,
        minLength: 8
      }
    },
    {
      key: 'country',
      type: 'select',
      templateOptions: {
        label: 'Country',
        required: true,
        options: [
          { value: 'us', label: 'United States' },
          { value: 'uk', label: 'United Kingdom' },
          { value: 'ca', label: 'Canada' },
          { value: 'au', label: 'Australia' }
        ]
      }
    },
    {
      key: 'interests',
      type: 'multicheckbox',
      templateOptions: {
        label: 'Interests',
        options: [
          { value: 'programming', label: 'Programming' },
          { value: 'design', label: 'Design' },
          { value: 'marketing', label: 'Marketing' },
          { value: 'business', label: 'Business' }
        ]
      }
    },
    {
      key: 'newsletter',
      type: 'checkbox',
      templateOptions: {
        label: 'Subscribe to newsletter'
      }
    },
    {
      key: 'terms',
      type: 'checkbox',
      templateOptions: {
        label: 'I agree to the terms and conditions',
        required: true
      }
    }
  ];

  onSubmit() {
    if (this.form.valid) {
      console.log('Registration Data:', this.model);
      alert('Registration successful!');
    } else {
      console.log('Form is invalid');
    }
  }
}

Resources for Further Learning

  • Official Documentation: Visit the Angular Formly documentation for comprehensive guides
  • Community Examples: Explore the examples section for real-world use cases
  • GitHub Repository: Check out the source code and contribute to the project
  • Stack Overflow: Ask questions and help others in the community
🚀 Ready for More?

Now that you understand the basics, try building a complete application with Angular Formly. Start with a simple contact form and gradually add more complex features like file uploads, dynamic field arrays, and custom validation.