Angular Pipes

Pipes transform values directly inside templates, and Angular ships a solid set of built-in ones alongside a small, well-defined API for writing your own.

What Pipes Do

A pipe takes an input value and returns a transformed output, applied in a template with the '|' syntax. Instead of formatting a date or currency value in component logic, you express the transformation right where it's displayed, which keeps templates declarative and components free of view-formatting code.

Common Built-in Pipes

  • DatePipe — formats JavaScript Date values ({{ date | date:'shortDate' }}).
  • CurrencyPipe — formats numbers as currency with a symbol and locale.
  • UpperCasePipe / LowerCasePipe — changes string casing.
  • DecimalPipe — controls the number of integer and decimal digits shown.
  • PercentPipe — formats a fraction as a percentage.
  • JsonPipe — pretty-prints an object, handy for debugging templates.
  • AsyncPipe — subscribes to an Observable or Promise and unwraps its value.
  • SlicePipe — returns a subset of an array or string.

pipe-demo.component.ts

import { Component } from '@angular/core';
import { CommonModule } from '@angular/common';
import { bootstrapApplication } from '@angular/platform-browser';

@Component({
  selector: 'app-pipe-demo',
  standalone: true,
  imports: [CommonModule],
  template: `
    <p>{{ today | date:'fullDate' }}</p>
    <p>{{ price | currency:'USD':'symbol':'1.2-2' }}</p>
    <p>{{ username | uppercase }}</p>
    <p>{{ ratio | percent:'1.0-1' }}</p>
  `
})
export class PipeDemoComponent {
  today = new Date();
  price = 49.9;
  username = 'ada lovelace';
  ratio = 0.734;
}

bootstrapApplication(PipeDemoComponent).catch((err) => console.error(err));

Chaining and Parameterizing Pipes

Pipes accept parameters after a colon, and multiple pipes can be chained with additional '|' characters — each pipe receives the output of the one before it, evaluated left to right.

ExpressionResult for example input
{{ '2026-07-16' | date:'MMM d, y' }}Jul 16, 2026
{{ 12.4 | currency:'EUR' }}€12.40
{{ 'hello world' | slice:0:5 | uppercase }}HELLO
{{ 0.5 | percent }}50%

Creating a Custom Pipe

A custom pipe is a class decorated with @Pipe that implements PipeTransform. The transform() method receives the bound value plus any parameters passed after the colon, and returns the value to render. Standalone pipes are imported directly into a component's imports array, just like standalone components.

truncate.pipe.ts

import { Component, Pipe, PipeTransform } from '@angular/core';
import { bootstrapApplication } from '@angular/platform-browser';

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

@Component({
  selector: 'app-root',
  standalone: true,
  imports: [TruncatePipe],
  template: `<p>{{ text | truncate:20 }}</p>`
})
export class AppComponent {
  text = 'This is a long piece of sample text used to demonstrate the truncate pipe.';
}

bootstrapApplication(AppComponent).catch((err) => console.error(err));

file-size.pipe.ts

import { Component, Pipe, PipeTransform } from '@angular/core';
import { bootstrapApplication } from '@angular/platform-browser';

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

@Pipe({
  name: 'fileSize',
  standalone: true
})
export class FileSizePipe implements PipeTransform {
  transform(bytes: number, precision = 1): string {
    if (!bytes) return '0 B';
    const units = ['B', 'KB', 'MB', 'GB', 'TB'];
    const exponent = Math.min(Math.floor(Math.log(bytes) / Math.log(1024)), units.length - 1);
    const value = bytes / Math.pow(1024, exponent);
    return `${value.toFixed(precision)} ${units[exponent]}`;
  }
}

@Component({
  selector: 'app-file-card',
  standalone: true,
  imports: [TruncatePipe, FileSizePipe],
  template: `
    <p>{{ description | truncate:40 }}</p>
    <p>{{ sizeInBytes | fileSize:2 }}</p>
  `
})
export class FileCardComponent {
  description = 'This changelog entry explains what changed in the latest release.';
  sizeInBytes = 3458201;
}

bootstrapApplication(FileCardComponent).catch((err) => console.error(err));
Note: By default, pipes are 'pure' — Angular only re-runs transform() when the input reference changes. This is fast, but means mutating an array or object in place won't trigger a re-render.
Note: Marking a pipe { pure: false } makes it re-run on every change detection cycle. Use impure pipes sparingly; they can quickly become a performance bottleneck on large templates.

Exercise: Angular Pipes

What symbol applies a pipe to a template expression?