Question
Angular ngClass Conditional Class Binding Explained
Question
I am using Angular to conditionally apply an active class to list items, but my template throws this error:
Cannot read property 'remove' of undefined at BrowserDomAdapter.removeClass
Here is the code:
<ol>
<li *ngClass="{ active: step === 'step1' }" (click)="step = 'step1'">Step1</li>
<li *ngClass="{ active: step === 'step2' }" (click)="step = 'step2'">Step2</li>
<li *ngClass="{ active: step === 'step3' }" (click)="step = 'step3'">Step3</li>
</ol>
What is wrong with this Angular code, and how should conditional class binding be written correctly?
Short Answer
By the end of this page, you will understand how Angular conditional class binding works, when to use ngClass versus [class.className], and why using the wrong binding syntax can cause runtime errors in templates.
Concept
Angular lets you add or remove CSS classes dynamically based on component state. This is called conditional class binding.
In Angular, ngClass is a directive, not a structural directive. That means it should be used like a normal property binding:
<li [ngClass]="{ active: condition }"></li>
A common mistake is to write *ngClass instead of [ngClass].
<li *ngClass="{ active: condition }"></li>
The * prefix is only for structural directives such as:
*ngIf*ngFor*ngSwitchCase
Structural directives change the DOM structure by creating or removing elements. ngClass does not create or remove elements. It only changes the classes on an existing element.
Because of that, Angular expects different behavior when it sees *. Using *ngClass makes Angular treat it like a structural directive, which leads to incorrect internal handling and can cause runtime errors like the one shown.
This matters in real programming because class bindings are used everywhere:
- highlighting active navigation items
- showing validation states
- toggling themes
- displaying selected tabs
- marking loading or error states
If the binding syntax is wrong, your UI can fail at runtime even though the idea is correct.
Mental Model
Think of an HTML element like a box that already exists on the page.
- A structural directive with
*decides whether the box exists at all. - A class binding decides what label or color sticker gets attached to that box.
So:
*ngIf= create or remove the box[ngClass]= keep the box, but change its CSS classes
Using *ngClass is like telling Angular: “Treat this class change like element creation logic.” That confuses Angular because ngClass is not built for that job.
Syntax and Examples
The correct syntax for conditional classes in Angular is:
<li [ngClass]="{ active: step === 'step1' }">Step 1</li>
You can also use direct class binding for a single class:
<li [class.active]="step === 'step1'">Step 1</li>
Corrected version of your example
<ol>
<li [ngClass]="{ active: step === 'step1' }" (click)="step = 'step1'">Step1</li>
<li [ngClass]="{ active: step === 'step2' }" (click)="step = 'step2'">Step2</li>
<li [ngClass]="{ active: step === 'step3' }" (click)="step = 'step3'">Step3</li>
</ol>
Simpler version for one class
<ol>
<li [class.active]="step === 'step1'" (click)="step = 'step1'">Step1</li>
<li [class.active]="step === 'step2'" (click)="step = 'step2'">Step2</li>
<li [class.active]="step === 'step3'" (click)="step = 'step3'">Step3</li>
</ol>
Component example
import { Component } from '@angular/core';
@Component({
selector: 'app-steps',
templateUrl: './steps.component.html'
})
export class StepsComponent {
step = 'step1';
}
Explanation
stepstores the current active step.- When a user clicks a list item,
stepis updated. - Angular re-evaluates the expression.
- The
activeclass is added only to the matching item.
Step by Step Execution
Consider this template:
<li [class.active]="step === 'step2'" (click)="step = 'step2'">Step2</li>
And this component state:
step = 'step1';
What happens step by step
-
Angular renders the
<li>element. -
It checks the expression:
step === 'step2' -
Since
stepis currently'step1', the result isfalse. -
Angular does not add the
activeclass. -
The user clicks
Step2. -
The click handler runs:
step = 'step2' -
Angular change detection runs again.
-
It re-checks the expression:
step === 'step2' -
Now the result is
true. -
Angular adds the
activeclass to that<li>.
Small trace example
initial step = 'step1'
check: 'step1' === 'step2' => false
class applied? no
click happens
step becomes 'step2'
check: 'step2' === 'step2' => true
class applied? yes
This is the basic pattern behind tabs, menus, selected items, and active states.
Real World Use Cases
Conditional class binding is used in many common Angular interfaces.
Navigation menus
Highlight the current page or selected section.
<a [class.active]="currentPage === 'home'">Home</a>
Tab components
Show which tab is selected.
<button [class.active]="selectedTab === 'profile'">Profile</button>
Form validation
Mark invalid inputs visually.
<input [class.invalid]="emailTouched && !emailValid">
Loading and status indicators
Apply styles based on state.
<div [ngClass]="{ loading: isLoading, error: hasError }"></div>
Selected items in a list
<li [class.selected]="item.id === selectedId">{{ item.name }}</li>
These patterns make the UI react automatically to component data.
Real Codebase Usage
In real Angular projects, developers often choose between [class.className] and [ngClass] based on complexity.
Use [class.className] for one class
This is the cleanest option when you only need to toggle a single class.
<button [class.disabled]="isSaving">Save</button>
Use [ngClass] for multiple classes
<div [ngClass]="{
active: isActive,
disabled: isDisabled,
error: hasError
}"></div>
Common patterns in real code
Guarding visual states
<div [class.hidden]="!user"></div>
Validation styling
<input [ngClass]="{
'is-invalid': control.touched && control.invalid,
'is-valid': control.touched && control.valid
}">
State-driven UI
<span [class.online]="user.status === 'online'"></span>
Reusable components
A component may expose inputs that drive classes:
@Input() isPrimary = false;
@Input() isLarge = false;
<button [ngClass]="{ primary: isPrimary, large: isLarge }">
Click
</button>
The main idea is that classes should come from state, not manual DOM manipulation.
Common Mistakes
1. Using *ngClass instead of [ngClass]
Broken code:
<li *ngClass="{ active: isActive }">Item</li>
Correct code:
<li [ngClass]="{ active: isActive }">Item</li>
Why it fails:
*is for structural directives.ngClassis an attribute directive.
2. Forgetting quotes around string values
Broken code:
<li [class.active]="step === step1">Step1</li>
Correct code:
<li [class.active]="step === 'step1'">Step1</li>
Without quotes, Angular treats step1 as a variable or property name.
3. Mixing assignment with comparison
Broken code:
<li [class.active]="step = 'step1'">Step1</li>
Correct code:
<li [class.active]="step === 'step1'">Step1</li>
Use === when checking a condition.
4. Using ngClass when simple class binding is enough
This is not wrong, but it can be unnecessarily verbose.
Verbose:
<li [ngClass]="{ active: isActive }">Item</li>
Cleaner:
<li [class.active]="isActive">Item</li>
5. Expecting CSS to work without defining the class
Angular may correctly apply the class, but if your stylesheet has no .active rule, nothing will look different.
Example:
.active {
font-weight: bold;
color: blue;
}
6. Using complex logic directly in templates
Possible but harder to maintain:
<div [class.active]="user && user.role === 'admin' && !isLocked && currentPage === 'dashboard'"></div>
Better:
get isAdminDashboardActive(): boolean {
return !!this.user && this.user.role === 'admin' && !this.isLocked && this.currentPage === 'dashboard';
}
<div [class.active]="isAdminDashboardActive"></div>
Comparisons
| Approach | Use case | Example | Notes |
|---|---|---|---|
[class.active] | Toggle one class | [class.active]="isActive" | Simple and readable |
[ngClass] with object | Toggle multiple classes | [ngClass]="{ active: isActive, error: hasError }" | Best for several conditions |
[ngClass] with string | Apply fixed or computed class names | [ngClass]="'active highlight'" | Useful when classes come from code |
[ngClass] with array | Combine several class values | [ngClass]="['active', sizeClass]" | Good for composition |
class="active" | Always apply a class | class="active" | No condition involved |
*ngIf | Add/remove element from DOM | *ngIf="isVisible" | Not a class binding |
*ngIf vs [ngClass]
| Feature | *ngIf | [ngClass] |
|---|---|---|
| Purpose | Create/remove element | Add/remove CSS classes |
Uses * | Yes | No |
| Changes DOM structure | Yes | No |
| Common use | Hide/show sections | Style states |
When to choose which
- Use
[class.active]for one class. - Use
[ngClass]for several classes. - Use
*ngIfwhen the element should not exist at all.
Cheat Sheet
<!-- One conditional class -->
<div [class.active]="isActive"></div>
<!-- Multiple conditional classes -->
<div [ngClass]="{ active: isActive, error: hasError }"></div>
<!-- Class from string -->
<div [ngClass]="currentClass"></div>
<!-- Class from array -->
<div [ngClass]="['btn', sizeClass]"></div>
Rules
- Use
[ngClass], not*ngClass - Use
*only with structural directives like*ngIfand*ngFor - Use
[class.className]for one class - Use
===to compare values in conditions - Quote string literals like
'step1'
Good examples
<li [class.active]="step === 'step1'"></li>
<li [ngClass]="{ active: step === 'step1' }"></li>
Bad example
<li *ngClass="{ active: step === 'step1' }"></li>
Quick fix for the original problem
<ol>
<li [ngClass]="{ active: step === 'step1' }" (click)="step = 'step1'">Step1</li>
<li [ngClass]="{ active: step === 'step2' }" (click)="step = 'step2'">Step2</li>
<li [ngClass]="{ active: step === 'step3' }" (click)="step = 'step3'">Step3</li>
</ol>
FAQ
Why does *ngClass cause an Angular error?
Because * tells Angular to treat the directive as structural, but ngClass is not structural. It should be bound with [ngClass].
Should I use [ngClass] or [class.active]?
Use [class.active] when toggling one class. Use [ngClass] when applying multiple classes or building classes dynamically.
Is ngClass the same as class?
No. class applies fixed classes, while ngClass lets Angular add or remove classes based on component data.
Can I use a function inside ngClass?
Yes, but keep template logic simple. If the function is expensive or unclear, compute the value in the component instead.
What is the difference between *ngIf and [ngClass]?
*ngIf adds or removes an element from the DOM. [ngClass] keeps the element and only changes its CSS classes.
Why is my class not visibly changing?
The class may be applied correctly, but your CSS may not define any styles for it. Check your stylesheet.
Do I need to initialize step in the component?
It is a good idea. Initializing step makes the initial UI state predictable and easier to debug.
Mini Project
Description
Build a simple Angular step navigation component that highlights the currently selected step. This project demonstrates conditional class binding, click event handling, and state-driven UI updates.
Goal
Create a three-step navigation where clicking a step updates the active class correctly.
Requirements
- Create a component with a
stepproperty - Render three clickable steps in the template
- Apply an
activeclass only to the selected step - Change the selected step when the user clicks an item
- Add CSS so the active step looks visually different
Keep learning
Related questions
CSS :not() Selector for Elements Without a Class or Attribute
Learn how to use the CSS :not() selector to target elements that do not have a specific class or attribute, with examples and common pitfalls.
CSS Font Scaling Relative to Container Size: %, em, rem, vw, and Responsive Text
Learn how CSS font scaling really works and how to make text responsive using %, em, rem, vw, clamp(), and media queries.
CSS Parent Selector Explained: Selecting a Parent <li> from an <a>
Learn whether CSS has a parent selector, how :has() works, and practical alternatives for styling a parent li from a child anchor.