A pipe takes a value and transforms it for display purposes, applied directly in the template with the | syntax — the underlying data in the component stays completely unchanged; only what's rendered on screen is affected.
1. Common built-in pipes
<p>{{ price | currency }}</p> <!-- $1,234.56 -->
<p>{{ publishedAt | date: 'mediumDate' }}</p> <!-- Mar 9, 2024 -->
<p>{{ title | uppercase }}</p>
<p>{{ description | slice: 0:100 }}...</p> <!-- truncate to the first 100 characters -->
<p>{{ user | json }}</p> <!-- pretty-printed JSON, mainly useful for debugging -->
2. Passing parameters to a pipe
{{ price | currency: 'EUR' }}
{{ price | currency: 'EUR': 'symbol': '1.2-2' }}
Parameters after the first colon customize the pipe's behavior — for currency, that includes the currency code, whether to show a symbol or code, and the minimum/maximum decimal digits, all without needing to hand-format the number in the component class.
3. Chaining multiple pipes together
{{ title | lowercase | titlecase }}
Pipes apply left to right — each one transforms the output of whatever came before it, so several small, focused pipes can compose into a more complex transformation without needing one pipe that tries to do everything.
4. Writing a custom pipe
ng generate pipe truncate
import { Pipe, PipeTransform } from '@angular/core';
@Pipe({
name: 'truncate',
})
export class TruncatePipe implements PipeTransform {
transform(value: string, maxLength: number = 100): string {
if (!value || value.length <= maxLength) {
return value;
}
return value.slice(0, maxLength).trim() + '…';
}
}
<p>{{ post.excerpt | truncate: 150 }}</p>
A custom pipe is a class implementing PipeTransform, with its actual logic in the required transform() method — the @Pipe decorator's name is what the template uses after the | symbol, and any additional method parameters beyond the piped value itself become the pipe's own configurable arguments in the template.
5. Pure vs. impure pipes — a real performance consideration
@Pipe({
name: 'filterActive',
pure: false, // opts into re-running on every change detection cycle
})
export class FilterActivePipe implements PipeTransform {
transform(items: Item[]): Item[] {
return items.filter((item) => item.isActive);
}
}
By default, a pipe is "pure" — Angular only re-runs it when its actual input reference changes, not on every change detection cycle. A pipe filtering or transforming an array (like the example above) needs to be pure to work correctly if the array's contents mutate in place without creating a new array reference — but marking a pipe impure (as shown) makes it re-run considerably more often, which is a real performance cost worth being deliberate about rather than reaching for by default whenever something "doesn't seem to update."
6. When a pipe is the wrong tool
A pipe is meant for lightweight, display-only formatting — for anything involving an actual API call, complex business logic, or a transformation expensive enough to matter performance-wise, that logic belongs in the component class (or a service) instead, computed once and stored as a property, rather than re-run repeatedly through a pipe in the template on every change detection pass.