Pure vs Impure Pipes in Angular

Pipes in Angular are a powerful feature that allow developers to transform data directly within the template, without writing extra logic in the component. They help keep templates clean, readable, and declarative. While most pipes in Angular are pure, there’s another category called impure pipes that can react to more frequent changes.

Understanding the difference between pure and impure pipes is essential for writing efficient and performant Angular applications. In this comprehensive post, we’ll explore what makes a pipe pure or impure, how Angular handles them internally, when to use each, and best practices to avoid performance issues.

1. Introduction to Pipes

In Angular, a pipe is a class that implements the PipeTransform interface. Pipes transform input values before displaying them in the view. For example, they can:

  • Format dates, numbers, or currencies.
  • Convert text to uppercase or lowercase.
  • Filter or sort arrays.
  • Perform custom transformations.

A simple example of using a pipe in a template:

<p>{{ username | uppercase }}</p>

Here, the uppercase pipe transforms the value of username into uppercase letters before rendering it.


2. Built-in Pipes Overview

Angular provides several built-in pipes such as:

  • DatePipe — formats date objects or timestamps.
  • CurrencyPipe — formats numbers as currency strings.
  • DecimalPipe — formats decimal numbers.
  • PercentPipe — formats numbers as percentages.
  • AsyncPipe — subscribes to Observables or Promises and updates automatically.
  • SlicePipe — extracts a subset of an array or string.

Example using multiple pipes together:

<p>{{ price | currency:'USD':'symbol':'1.2-2' }}</p>
<p>{{ today | date:'fullDate' }}</p>
<p>{{ user.name | uppercase | slice:0:5 }}</p>

All these built-in pipes are pure by default.


3. What are Pure Pipes?

A pure pipe in Angular executes only when it detects a change in the input value reference passed to it. That means Angular does not call the pipe’s transform() method during every change detection cycle — only when the pipe’s input changes.

By default, all pipes in Angular are pure.

You can define a pure pipe like this:

import { Pipe, PipeTransform } from '@angular/core';

@Pipe({
  name: 'square'
})
export class SquarePipe implements PipeTransform {
  transform(value: number): number {
console.log('Pure Pipe Executed');
return value * value;
} }

And use it in a template:

<p>{{ 4 | square }}</p>

3.1. Characteristics of Pure Pipes

  • They are stateless — the output depends only on the input.
  • They execute only when the input reference changes.
  • They are memoized — Angular may cache the previous result.
  • They are fast and efficient for performance.

4. When Does a Pure Pipe Re-run?

A pure pipe is triggered only when Angular detects that the input reference has changed. Let’s consider an example.

@Component({
  selector: 'app-user-list',
  template: `
&lt;p&gt;{{ users | json }}&lt;/p&gt;
&lt;p&gt;Filtered Users: {{ users | filterPipe:'active' }}&lt;/p&gt;
` }) export class UserListComponent { users = [{ name: 'Ali', status: 'active' }, { name: 'Sara', status: 'inactive' }]; }

Now suppose we add a new user to the same array:

addUser() {
  this.users.push({ name: 'John', status: 'active' });
}

Even though the array content changed, the reference did not.
Hence, the pure pipe will not re-run, and the template will not update automatically.

However, if we create a new array reference:

addUser() {
  this.users = [...this.users, { name: 'John', status: 'active' }];
}

Now the reference of users has changed, so the pure pipe executes again, and the updated result is displayed.


5. What are Impure Pipes?

An impure pipe executes on every change detection cycle, regardless of whether the input data reference changes. It’s useful when the data is mutable or changes frequently without changing the reference, such as in cases with:

  • Arrays or objects that are updated directly.
  • Real-time data streams.
  • Frequently updated UI states.

To define an impure pipe, set pure: false in the @Pipe decorator:

import { Pipe, PipeTransform } from '@angular/core';

@Pipe({
  name: 'impurePipe',
  pure: false
})
export class ImpurePipe implements PipeTransform {
  transform(value: any[]): any[] {
console.log('Impure Pipe Executed');
return value.filter(v =&gt; v.active);
} }

5.1. Usage Example

<p>Active Users: {{ users | impurePipe | json }}</p>

Whenever Angular runs change detection (which happens frequently), the impure pipe will execute again, even if the input data did not change.


6. Pure vs Impure Pipes: Key Differences

FeaturePure PipeImpure Pipe
ExecutionRuns only when input reference changesRuns on every change detection cycle
PerformanceHigh performanceCan impact performance
Default Behaviorpure: true (default)Must be set manually
Use CaseStatic or immutable dataDynamic or mutable data
CachingPossible due to immutabilityNo caching (always recalculates)
ExampleCurrencyPipe, DatePipeCustom filtering or dynamic state pipes

7. Example: Comparing Pure and Impure Behavior

Let’s understand with a practical example.

7.1. Pure Pipe Example

@Pipe({
  name: 'pureFilter'
})
export class PureFilterPipe implements PipeTransform {
  transform(items: any[], status: string): any[] {
console.log('Pure Filter Pipe Executed');
return items.filter(item =&gt; item.status === status);
} }

Usage:

<p>{{ users | pureFilter:'active' | json }}</p>
<button (click)="addUser()">Add User</button>

Component:

export class AppComponent {
  users = [{ name: 'Ali', status: 'active' }];

  addUser() {
this.users.push({ name: 'Sara', status: 'active' });
} }

In this case, the new user will not appear in the output because the array reference did not change.


7.2. Impure Pipe Example

Now modify the pipe as:

@Pipe({
  name: 'impureFilter',
  pure: false
})
export class ImpureFilterPipe implements PipeTransform {
  transform(items: any[], status: string): any[] {
console.log('Impure Filter Pipe Executed');
return items.filter(item =&gt; item.status === status);
} }

Use it the same way:

<p>{{ users | impureFilter:'active' | json }}</p>

Now, when addUser() pushes a new object to the array, the impure pipe executes automatically on the next change detection and updates the result.


8. How Angular Handles Pipe Execution

Angular uses a change detection mechanism to keep the DOM synchronized with application data. Every time change detection runs (for example, due to user input, HTTP response, or timer), Angular checks whether the bound data has changed.

  • For pure pipes, Angular checks if the input reference has changed.
  • For impure pipes, Angular skips the check and executes transform() every time.

Internally, this is why impure pipes can be performance-heavy — they execute many times even when the data hasn’t changed.


9. When to Use Impure Pipes

Use impure pipes only when necessary. Some scenarios where they make sense include:

  1. Mutable Data Structures:
    When objects or arrays are modified directly instead of replaced.
  2. Real-time Streams:
    When values update continuously (e.g., live data dashboards).
  3. Complex Filtering:
    When you must react to UI events or frequent changes not tied to immutable data.
  4. Temporary Development/Debugging:
    When testing frequent UI updates during development.

10. Performance Considerations

Impure pipes can become a major performance bottleneck if used carelessly, especially in large applications. Every impure pipe re-runs during every change detection cycle, which can be dozens or hundreds of times per second.

10.1. Avoid Overuse

Limit impure pipes to specific cases. If you can achieve the same result with:

  • Component logic (ngOnChanges, Observables)
  • RxJS operators (map, filter)
  • OnPush change detection strategy

— then prefer those approaches.

10.2. Example of Inefficient Usage

<p *ngFor="let user of users | impureFilter:'active'">{{ user.name }}</p>

If users is large and the pipe runs constantly, this will slow down the UI significantly.


11. Best Practices for Using Pipes

Here are key best practices for using pipes efficiently in Angular:

  1. Prefer Pure Pipes (Default):
    Use pure pipes wherever possible for better performance.
  2. Avoid Mutating Data:
    Always create new references when updating arrays or objects. this.users = [...this.users, newUser];
  3. Use Impure Pipes Sparingly:
    Only use pure: false when absolutely necessary.
  4. Keep Pipes Simple:
    Avoid complex computations or API calls inside pipes.
  5. Consider Change Detection Strategy:
    Combine pipes with ChangeDetectionStrategy.OnPush for efficient rendering.
  6. Avoid Side Effects:
    Pipes should be pure functions — they should not modify inputs or trigger external effects.

12. Alternative to Impure Pipes

Instead of relying on impure pipes for mutable data, consider these alternatives:

12.1. Use Observables with AsyncPipe

The AsyncPipe automatically updates the template when new data is emitted.

<p>{{ users$ | async | json }}</p>

This eliminates the need for impure custom filtering.


12.2. Handle Filtering in the Component

Instead of using a pipe, perform filtering in the component logic:

get activeUsers() {
  return this.users.filter(u => u.status === 'active');
}

Template:

<p>{{ activeUsers | json }}</p>

This approach provides more control and avoids unnecessary pipe re-execution.


13. Debugging Pipe Behavior

When debugging, you can log inside the transform() method to observe when a pipe executes.

Example:

transform(value: any): any {
  console.log('Pipe called with value:', value);
  return modifyValue(value);
}

This helps confirm whether your pipe is pure or impure and how often it’s running.


14. Real-World Example

Imagine an application that displays real-time stock prices. The data updates several times per second.

If you used a pure pipe, the values wouldn’t refresh because the input reference remains the same.
In this case, an impure pipe may be justified.

However, for most business applications (e.g., filtering lists, formatting data), pure pipes are preferred for performance and maintainability.


Comments

Leave a Reply

Your email address will not be published. Required fields are marked *