← Back to Articles         
Dynamic Controls and Rows Using FormArray in Angular

Dynamic Controls and Rows Using FormArray in Angular

Published on 01 Oct 2026     9 min read Angular
Angular forms

Dynamic Controls and Rows Using FormArray in Angular

These two questions are closely related:

  1. How do you dynamically add controls?
  2. How do you dynamically add rows using FormArray?

The key Angular class is FormArray.


1. How Do You Dynamically Add Controls?

A dynamic control is a form control that is added or removed at runtime, usually based on user action.

For example:

Skills

[C#        ] [Remove]
[Angular   ] [Remove]
[SQL       ] [Remove]

[+ Add Skill]

We don't know in advance how many skills the user will enter.

Therefore, instead of creating:

skill1
skill2
skill3

we use:

FormArray

Conceptually:

FormArray
│
├── FormControl → C#
├── FormControl → Angular
├── FormControl → SQL
└── FormControl → Azure

2. Full Sample – Dynamically Add Controls

app.ts

import { Component } from '@angular/core';
import {
  FormArray,
  FormControl,
  FormGroup,
  ReactiveFormsModule,
  Validators
} from '@angular/forms';

@Component({
  selector: 'app-root',
  standalone: true,
  imports: [ReactiveFormsModule],
  templateUrl: './app.html'
})
export class App {

  employeeForm = new FormGroup({

    name: new FormControl('', Validators.required),

    skills: new FormArray([
      new FormControl('', Validators.required)
    ])

  });

  get skills(): FormArray {
    return this.employeeForm.controls.skills;
  }

  addSkill() {

    this.skills.push(
      new FormControl('', Validators.required)
    );

  }

  removeSkill(index: number) {

    this.skills.removeAt(index);

  }

  submitForm() {

    if (this.employeeForm.valid) {

      console.log(this.employeeForm.value);

    } else {

      this.employeeForm.markAllAsTouched();

    }

  }
}

app.html

<h2>Employee Form</h2>

<form
  [formGroup]="employeeForm"
  (ngSubmit)="submitForm()">

  <div>

    <label>Employee Name:</label>

    <input
      type="text"
      formControlName="name">

  </div>

  <br>

  <h3>Skills</h3>

  <div formArrayName="skills">

    @for (skill of skills.controls; track $index) {

      <div>

        <input
          type="text"
          [formControlName]="$index">

        <button
          type="button"
          (click)="removeSkill($index)">

          Remove

        </button>

      </div>

      <br>

    }

  </div>

  <button
    type="button"
    (click)="addSkill()">

    Add Skill

  </button>

  <br><br>

  <button type="submit">
    Submit
  </button>

</form>

3. Explanation

We create a FormArray:

skills: new FormArray([
  new FormControl('', Validators.required)
])

Initially:

skills FormArray
       │
       └── FormControl

The form displays:

Skills

[             ]

[Add Skill]

Adding a Control

When the user clicks:

Add Skill

Angular executes:

addSkill() {

  this.skills.push(
    new FormControl('', Validators.required)
  );

}

push() adds another control to the FormArray.

After clicking three times:

FormArray
│
├── FormControl
├── FormControl
├── FormControl
└── FormControl

The UI automatically renders those controls.


Removing a Control

We use:

removeSkill(index: number) {
  this.skills.removeAt(index);
}

Suppose:

Index

0 → C#
1 → Angular
2 → SQL

Calling:

this.skills.removeAt(1);

removes:

Angular

Result:

0 → C#
1 → SQL

4. What is formArrayName?

In HTML:

<div formArrayName="skills">

This connects the HTML section to:

skills: new FormArray(...)

Then each control is connected using its index:

<input [formControlName]="$index">

Conceptually:

FormArray

skills[0] ←→ Input 1
skills[1] ←→ Input 2
skills[2] ←→ Input 3

5. Dynamically Adding Controls vs Rows

There is an important distinction.

Dynamic controls

Each item is just one FormControl:

Skills

[C#]
[Angular]
[SQL]

Structure:

FormArray
│
├── FormControl
├── FormControl
└── FormControl

Dynamic rows

Each row contains multiple fields:

Product       Quantity       Price

[Laptop]      [2]            [50000]
[Mouse ]      [3]            [1000]
[Keyboard]    [1]            [2000]

[+ Add Row]

Each row should therefore be a FormGroup.

Structure:

FormArray
│
├── FormGroup
│    ├── product
│    ├── quantity
│    └── price
│
├── FormGroup
│    ├── product
│    ├── quantity
│    └── price
│
└── FormGroup
     ├── product
     ├── quantity
     └── price

This is a very common real-world use of FormArray.


6. Full Sample – Dynamically Add Rows Using FormArray

Let's create an order form.

Each row contains:

Product | Quantity | Price | Remove

app.ts

import { Component } from '@angular/core';
import {
  FormArray,
  FormControl,
  FormGroup,
  ReactiveFormsModule,
  Validators
} from '@angular/forms';

@Component({
  selector: 'app-root',
  standalone: true,
  imports: [ReactiveFormsModule],
  templateUrl: './app.html'
})
export class App {

  orderForm = new FormGroup({

    customerName: new FormControl('', Validators.required),

    items: new FormArray([
      this.createItem()
    ])

  });

  createItem(): FormGroup {

    return new FormGroup({

      product: new FormControl('', Validators.required),

      quantity: new FormControl(1, [
        Validators.required,
        Validators.min(1)
      ]),

      price: new FormControl(0, [
        Validators.required,
        Validators.min(1)
      ])

    });

  }

  get items(): FormArray {

    return this.orderForm.controls.items;

  }

  addItem() {

    this.items.push(
      this.createItem()
    );

  }

  removeItem(index: number) {

    this.items.removeAt(index);

  }

  submitForm() {

    if (this.orderForm.valid) {

      console.log(this.orderForm.value);

    } else {

      this.orderForm.markAllAsTouched();

    }

  }
}

app.html

<h2>Order Form</h2>

<form
  [formGroup]="orderForm"
  (ngSubmit)="submitForm()">

  <div>

    <label>Customer Name:</label>

    <input
      type="text"
      formControlName="customerName">

  </div>

  <br>

  <h3>Order Items</h3>

  <div formArrayName="items">

    @for (item of items.controls; track $index) {

      <div [formGroupName]="$index">

        <label>Product:</label>

        <input
          type="text"
          formControlName="product">

        <label>Quantity:</label>

        <input
          type="number"
          formControlName="quantity">

        <label>Price:</label>

        <input
          type="number"
          formControlName="price">

        <button
          type="button"
          (click)="removeItem($index)">

          Remove

        </button>

      </div>

      <br>

    }

  </div>

  <button
    type="button"
    (click)="addItem()">

    Add Row

  </button>

  <br><br>

  <button type="submit">
    Submit Order
  </button>

</form>

7. Understanding createItem()

The important method is:

createItem(): FormGroup {

  return new FormGroup({

    product: new FormControl('', Validators.required),

    quantity: new FormControl(1),

    price: new FormControl(0)

  });

}

This method creates one row.

Think:

createItem()

      ↓

FormGroup
│
├── product
├── quantity
└── price

Every time we need another row, we call:

this.createItem()

8. Adding a Row

Our method:

addItem() {

  this.items.push(
    this.createItem()
  );

}

Suppose initially:

FormArray
    │
    └── Row 1

User clicks Add Row:

FormArray
    │
    ├── Row 1
    └── Row 2

Clicks again:

FormArray
    │
    ├── Row 1
    ├── Row 2
    └── Row 3

Each row is a separate FormGroup.


9. Removing a Row

We use:

removeItem(index: number) {

  this.items.removeAt(index);

}

Suppose:

0 → Laptop
1 → Mouse
2 → Keyboard

Calling:

removeItem(1);

removes the Mouse row.

Result:

0 → Laptop
1 → Keyboard

10. Understanding the HTML Structure

This part is especially important:

<div formArrayName="items">

  @for (item of items.controls; track $index) {

    <div [formGroupName]="$index">

      <input formControlName="product">

      <input formControlName="quantity">

      <input formControlName="price">

    </div>

  }

</div>

It exactly follows the TypeScript structure.

TypeScript

orderForm
│
└── items                  ← FormArray
     │
     ├── [0]               ← FormGroup
     │    ├── product
     │    ├── quantity
     │    └── price
     │
     └── [1]               ← FormGroup
          ├── product
          ├── quantity
          └── price

HTML

formGroup="orderForm"

      ↓

formArrayName="items"

      ↓

formGroupName="0"

      ↓

formControlName="product"
formControlName="quantity"
formControlName="price"

That relationship is the key to understanding dynamic rows.


11. Submitted Data

Suppose the user enters:

Customer: Syed

Product       Quantity     Price

Laptop           1        50000
Mouse            2         1000
Keyboard         1         2000

Then:

this.orderForm.value

will look approximately like:

{
  customerName: 'Syed',

  items: [
    {
      product: 'Laptop',
      quantity: 1,
      price: 50000
    },
    {
      product: 'Mouse',
      quantity: 2,
      price: 1000
    },
    {
      product: 'Keyboard',
      quantity: 1,
      price: 2000
    }
  ]
}

This structure maps naturally to a typical ASP.NET Core request DTO:

Order
│
├── CustomerName
│
└── Items[]
     ├── Product
     ├── Quantity
     └── Price

12. Important FormArray Methods

Add

this.items.push(control);

Remove

this.items.removeAt(index);

Insert at a specific position

this.items.insert(
  1,
  this.createItem()
);

Get a control

this.items.at(0);

Number of items

this.items.length;

Clear all items

this.items.clear();

So the commonly used methods are:

push()      → Add at end

insert()    → Add at position

removeAt()  → Remove one

at()        → Get one

clear()     → Remove all

length      → Number of items

13. FormControl vs FormGroup Inside FormArray

This is an important interview concept.

FormArray<FormControl>

Use when each dynamic item contains one value.

Example:

Skills

[C#]
[Angular]
[SQL]

Structure:

FormArray
│
├── FormControl
├── FormControl
└── FormControl

FormArray<FormGroup>

Use when each dynamic item contains multiple related values.

Example:

Product     Qty     Price

Laptop       1      50000
Mouse        2       1000

Structure:

FormArray
│
├── FormGroup
│    ├── product
│    ├── quantity
│    └── price
│
└── FormGroup
     ├── product
     ├── quantity
     └── price

Easy rule

One value per row → FormControl
Multiple values per row → FormGroup


14. Complete Flow

Page loads
    ↓
FormArray contains one FormGroup
    ↓
One row displayed
    ↓
User clicks "Add Row"
    ↓
addItem()
    ↓
createItem()
    ↓
New FormGroup created
    ↓
FormArray.push()
    ↓
FormArray contains another group
    ↓
@for detects it
    ↓
New row displayed

Removing works in reverse:

User clicks Remove
      ↓
removeItem(index)
      ↓
FormArray.removeAt(index)
      ↓
FormGroup removed
      ↓
Row disappears

15. Key Points

  • FormArray is designed for dynamic collections of controls.
  • Use push() to dynamically add a control or group.
  • Use removeAt(index) to remove one.
  • Use insert() to add at a particular index.
  • Use clear() to remove all items.
  • formArrayName connects the HTML to a FormArray.
  • [formControlName]="$index" is useful when the array contains individual FormControls.
  • [formGroupName]="$index" is used when each array element is a FormGroup.
  • Use FormArray<FormControl> for simple dynamic lists such as skills.
  • Use FormArray<FormGroup> for dynamic rows containing multiple fields.
  • A helper method such as createItem() keeps dynamic-row creation clean and reusable.

16. Interview Questions and Answers

1. How do you dynamically add controls in Angular Reactive Forms?

Use a FormArray and add controls using push().

this.skills.push(
  new FormControl('')
);

2. How do you remove a dynamic control?

Use:

this.skills.removeAt(index);

3. How do you dynamically add rows?

Create each row as a FormGroup and add that group to a FormArray.

this.items.push(
  this.createItem()
);

4. Why use FormArray instead of FormGroup for dynamic rows?

A FormArray is appropriate when the number of items is dynamic and indexed.

items[0]
items[1]
items[2]
...

A FormGroup is typically used for a known set of named controls.


5. Can a FormArray contain FormGroups?

Yes. This is one of the most common patterns for dynamic rows.

FormArray
│
├── FormGroup
├── FormGroup
└── FormGroup

6. Can a FormArray contain FormControls?

Yes.

For example, a dynamic skills list:

FormArray
│
├── FormControl → C#
├── FormControl → Angular
└── FormControl → SQL

7. What is formArrayName?

formArrayName connects an HTML section to a FormArray inside the parent form.

<div formArrayName="items">

8. Why do we use [formGroupName]="$index"?

When each FormArray item is a FormGroup, its index identifies which group the HTML row should bind to.

<div [formGroupName]="$index">

9. What is the difference between push() and insert()?

this.items.push(control);

adds the control at the end.

this.items.insert(1, control);

adds it at a specific index.


10. When should you use FormArray?

Use FormArray when the number of controls is not fixed, such as:

  • Skills
  • Phone numbers
  • Addresses
  • Qualifications
  • Employees
  • Invoice items
  • Order items
  • Employment history

Interview Summary

To dynamically add controls in Angular Reactive Forms, use a FormArray and its push() method. For a simple dynamic list, the FormArray can contain FormControls. For dynamic rows containing multiple fields, each row is represented by a FormGroup, and those groups are added to the FormArray. Use removeAt() to remove controls or rows dynamically.

← Back to Articles