Skip to content

Angular Custom Pipes

When Angular's built-in pipes don't cover a formatting need specific to your app, you can build your own. This lesson walks through creating, parameterizing, and testing a custom pipe.

What Is a Custom Pipe?

A custom pipe is a class decorated with @Pipe that implements the PipeTransform interface's transform() method, taking an input value (and optional arguments) and returning the transformed output for display.

Custom pipes are ideal for formatting logic specific to your domain, truncating long text with an ellipsis, converting a status code into a human-readable label, or formatting a custom unit of measurement.

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

@Pipe({
  name: 'truncate',
  standalone: true,
})
export class TruncatePipe implements PipeTransform {
  transform(value: string, limit = 20): string {
    if (!value || value.length <= limit) return value;
    return value.slice(0, limit).trimEnd() + '...';
  }
}

Usage in a template: {{ description | truncate:40 }} truncates description to 40 characters, appending an ellipsis.

The @Pipe Decorator and PipeTransform

@Pipe({
  name: 'pipeName',
  standalone: true,
  pure: true, // default
})
export class MyPipe implements PipeTransform {
  transform(value: InputType, ...args: unknown[]): OutputType {
    // return transformed value
  }
}
  • name is the identifier used in templates after the | operator.
  • PipeTransform requires implementing a single transform() method.
  • The first transform() parameter is always the piped-in value; the rest are the pipe's arguments.
  • standalone: true lets the pipe be imported directly by any component that needs it.

Custom Pipes Cheatsheet

Key pieces of a well-built custom pipe.

Concept Example Notes
Decorator @Pipe({ name: 'truncate' }) Registers the pipe's template name
Interface implements PipeTransform Enforces a transform() method
Method signature transform(value, ...args) First arg is the piped value
Usage {{ text | truncate:30 }} 30 becomes the second transform() parameter
Pure (default) pure: true Re-runs only when input reference changes
Impure pure: false Re-runs on every change detection cycle
CLI generate ng g pipe truncate Scaffolds pipe + spec file

A Pipe With Multiple Arguments

Pipes can accept more than one argument, each separated by a colon in the template, useful for pipes that need several configuration values.

@Pipe({ name: 'range', standalone: true })
export class RangePipe implements PipeTransform {
  transform(value: number, min: number, max: number): string {
    if (value < min) return `below ${min}`;
    if (value > max) return `above ${max}`;
    return 'within range';
  }
}

<!-- usage -->
<p>{{ score | range:0:100 }}</p>

Example: A Status Label Pipe

A very common real-world use case is converting an internal status code into a friendly, human-readable label everywhere it's displayed in the UI.

type OrderStatus = 'pending' | 'shipped' | 'delivered' | 'cancelled';

@Pipe({ name: 'orderStatusLabel', standalone: true })
export class OrderStatusLabelPipe implements PipeTransform {
  private labels: Record<OrderStatus, string> = {
    pending: 'Pending',
    shipped: 'Shipped',
    delivered: 'Delivered',
    cancelled: 'Cancelled',
  };

  transform(status: OrderStatus): string {
    return this.labels[status] ?? status;
  }
}

Testing a Custom Pipe

Because pipes are plain classes with a single transform() method, they are simple to unit test without any Angular testing utilities, just instantiate the class and call the method directly.

describe('TruncatePipe', () => {
  it('truncates long strings and appends an ellipsis', () => {
    const pipe = new TruncatePipe();
    expect(pipe.transform('Hello there, world!', 8)).toBe('Hello th...');
  });
});

Common Mistakes

  • Performing expensive computations (like API calls) inside transform(), which can run frequently.
  • Forgetting standalone: true (or module declaration) so the pipe can be imported where needed.
  • Not marking pipes pure: false when they genuinely need to react to internal, non-reference state changes.
  • Duplicating the same formatting logic across multiple components instead of extracting a shared custom pipe.

Key Takeaways

  • Custom pipes implement PipeTransform and its single transform() method.
  • The first transform() argument is the piped value; subsequent arguments come from the template after colons.
  • Custom pipes are easy to unit test since they're plain classes with no special Angular test setup required.
  • The Angular CLI's ng g pipe scaffolds a correctly structured pipe and spec file.

Pro Tip

Keep custom pipes pure and side-effect free. If a transformation needs live, streaming data (like a WebSocket feed), prefer combining it with the built-in async pipe rather than building a stateful impure pipe.