Pipes Overview
Pipes are Angular’s declarative way to transform a value for display without changing the data behind it. You apply one in a template with the pipe operator |, reading like a sentence: take this value, send it through that transformer, render the result. They keep components lean — formatting logic stays in the template where the formatting happens — and the framework caches their output efficiently. Almost every template you write will lean on a pipe for dates, currency, casing, or async values.
The pipe operator
A pipe takes the expression on its left, passes it to the pipe named on the right, and the pipe returns a transformed value. The original data is untouched; only what the user sees changes.
import { Component } from '@angular/core';
import { UpperCasePipe, DatePipe, CurrencyPipe } from '@angular/common';
@Component({
selector: 'app-receipt',
standalone: true,
imports: [UpperCasePipe, DatePipe, CurrencyPipe],
template: `
<p>{{ name | uppercase }}</p>
<p>{{ issued | date:'mediumDate' }}</p>
<p>{{ total | currency:'USD' }}</p>
`,
})
export class ReceiptComponent {
name = 'invoice #2048';
issued = new Date('2026-06-14');
total = 42.5;
}
Output:
INVOICE #2048
Jun 14, 2026
$42.50
In standalone components, each built-in pipe is a self-contained class you import into the component’s imports array. The name, issued, and total properties stay in their natural form on the class — strings and a Date — and only the rendered text is transformed.
Passing parameters
Many pipes accept arguments. You supply them after the pipe name with a colon, and you can pass several by chaining more colons. Arguments are ordinary template expressions, so they can be literals or component properties.
<!-- format string -->
<p>{{ issued | date:'fullDate' }}</p>
<!-- currency code, display style, and digit format -->
<p>{{ total | currency:'EUR':'symbol':'1.2-2' }}</p>
<!-- argument from a component property -->
<p>{{ total | currency:currencyCode }}</p>
Chaining pipes
The output of one pipe can feed straight into another. Angular applies them left to right, which makes it easy to combine transformations.
<p>{{ issued | date:'fullDate' | uppercase }}</p>
Output:
SUNDAY, JUNE 14, 2026
Here date produces a formatted string and uppercase then capitalises it. Read chains as a pipeline: each stage receives the previous stage’s result.
Pipes versus methods
You could format values by calling a method in the template — {{ formatPrice(total) }} — but pipes are usually the better tool. The crucial difference is change detection. A pure pipe (the default) only re-runs when its input reference changes, so Angular skips it on most change-detection cycles. A method, by contrast, has no such guarantee and is re-invoked on every cycle, which can run thousands of times per second.
| Aspect | Pipe | Method in template |
|---|---|---|
| Re-evaluation | Only when input changes (pure) | Every change-detection cycle |
| Caching | Built in for pure pipes | None |
| Reuse | Importable across components | Tied to one component |
| Readability | Declarative value | pipe | Imperative call |
| Composition | Chainable with | | Manual nesting |
Avoid calling methods in templates for anything you display repeatedly. A pure pipe gives you the same result with automatic memoisation, so prefer a pipe whenever the transformation depends only on its inputs.
Where pipes can be used
Pipes work anywhere a template expression is evaluated: inside interpolation {{ }} and inside property bindings.
<!-- interpolation -->
<span>{{ score | percent }}</span>
<!-- property binding -->
<input [value]="name | titlecase" />
Pipes cannot be used in the template statements that handle events (the right-hand side of (click)="..."), because those run imperative code rather than producing a displayed value.
Built-in and custom pipes
Angular ships a set of common pipes in @angular/common, including date, currency, number, percent, uppercase, lowercase, titlecase, slice, json, keyvalue, and the special async pipe for observables and promises. When none fits, you can write your own with the @Pipe decorator and a transform method.
import { Pipe, PipeTransform } from '@angular/core';
@Pipe({ name: 'truncate', standalone: true })
export class TruncatePipe implements PipeTransform {
transform(value: string, limit = 20): string {
return value.length > limit ? value.slice(0, limit) + '…' : value;
}
}
<p>{{ description | truncate:10 }}</p>
The first parameter of transform is always the piped value; any further parameters map to the colon-separated arguments in the template.
Best Practices
- Prefer pipes over template method calls for display formatting, so Angular can cache results and skip needless recomputation.
- Keep
transformlogic pure and side-effect free; a pipe should compute output solely from its inputs. - Import only the built-in pipes a component actually uses into its
importsarray for clear, tree-shakeable dependencies. - Reach for built-in pipes first (
date,currency,number) before writing a custom one — they handle locale and edge cases for you. - Use the
asyncpipe to subscribe to observables in the template; it unsubscribes automatically and avoids manual lifecycle code. - Be deliberate before marking a pipe impure — it runs on every change-detection cycle and can hurt performance.