What Are Pipes?
Pipes are simple functions that transform data for display in templates. They take a value, process it, and return a formatted result. Pipes are used with the | operator in template expressions.
{{ value | pipeName }}
{{ value | pipeName:arg1:arg2 }}Built-in Pipes
Angular provides several useful pipes out of the box:
DatePipe
Formats dates according to locale rules:
<p>{{ today | date }}</p>
<!-- "Feb 18, 2026" -->
<p>{{ today | date:'fullDate' }}</p>
<!-- "Wednesday, February 18, 2026" -->
<p>{{ today | date:'shortTime' }}</p>
<!-- "3:30 PM" -->
<p>{{ today | date:'yyyy-MM-dd HH:mm' }}</p>
<!-- "2026-02-18 15:30" -->
<p>{{ today | date:'EEEE, MMMM d' }}</p>
<!-- "Wednesday, February 18" -->| Format | Output Example |
|---|---|
'short' | 2/18/26, 3:30 PM |
'medium' | Feb 18, 2026, 3:30:00 PM |
'long' | February 18, 2026 at 3:30:00 PM |
'fullDate' | Wednesday, February 18, 2026 |
'shortDate' | 2/18/26 |
'shortTime' | 3:30 PM |
CurrencyPipe
Formats numbers as currency:
<p>{{ price | currency }}</p>
<!-- "$1,234.50" -->
<p>{{ price | currency:'EUR' }}</p>
<!-- "€1,234.50" -->
<p>{{ price | currency:'INR':'symbol':'1.0-0' }}</p>
<!-- "₹1,235" -->
<p>{{ price | currency:'GBP':'code' }}</p>
<!-- "GBP1,234.50" -->DecimalPipe
Formats numbers with decimal places:
<p>{{ 3.14159 | number:'1.2-2' }}</p>
<!-- "3.14" -->
<p>{{ 1234567 | number }}</p>
<!-- "1,234,567" -->
<p>{{ 0.756 | number:'1.0-0' }}</p>
<!-- "1" -->The format is minIntegerDigits.minFractionDigits-maxFractionDigits.
PercentPipe
<p>{{ 0.85 | percent }}</p>
<!-- "85%" -->
<p>{{ 0.8567 | percent:'1.1-1' }}</p>
<!-- "85.7%" -->UpperCasePipe / LowerCasePipe / TitleCasePipe
<p>{{ 'hello world' | uppercase }}</p>
<!-- "HELLO WORLD" -->
<p>{{ 'HELLO WORLD' | lowercase }}</p>
<!-- "hello world" -->
<p>{{ 'hello world' | titlecase }}</p>
<!-- "Hello World" -->SlicePipe
Extracts a portion of a string or array:
<p>{{ 'Angular Framework' | slice:0:7 }}</p>
<!-- "Angular" -->
@for (item of items | slice:0:5; track item.id) {
<p>{{ item.name }}</p>
}
<!-- Shows only first 5 items -->JsonPipe
Serializes an object to JSON — great for debugging:
<pre>{{ user | json }}</pre>
<!--
{
"name": "John",
"email": "john@example.com",
"role": "admin"
}
-->KeyValuePipe
Iterates over object key-value pairs:
@for (entry of config | keyvalue; track entry.key) {
<p>{{ entry.key }}: {{ entry.value }}</p>
}config = {
theme: 'dark',
language: 'en',
notifications: true,
};AsyncPipe
Subscribes to an Observable or Promise and returns the latest emitted value:
<p>{{ userData$ | async }}</p>
@if (users$ | async; as users) {
@for (user of users; track user.id) {
<p>{{ user.name }}</p>
}
} @else {
<p>Loading...</p>
}The async pipe automatically subscribes and unsubscribes, preventing memory leaks.
Chaining Pipes
Pipes can be chained — the output of one feeds into the next:
<p>{{ birthday | date:'fullDate' | uppercase }}</p>
<!-- "WEDNESDAY, FEBRUARY 18, 2026" -->
<p>{{ user.name | titlecase | slice:0:10 }}</p>Creating Custom Pipes
Simple Transform Pipe
import { Pipe, PipeTransform } from '@angular/core';
@Pipe({
name: 'truncate',
standalone: true,
})
export class TruncatePipe implements PipeTransform {
transform(value: string, limit: number = 50, trail: string = '...'): string {
if (!value) return '';
if (value.length <= limit) return value;
return value.substring(0, limit) + trail;
}
}Usage:
<p>{{ article.content | truncate }}</p> <!-- Default 50 chars -->
<p>{{ article.content | truncate:100 }}</p> <!-- 100 chars -->
<p>{{ article.content | truncate:80:'…' }}</p> <!-- Custom trail -->Time Ago Pipe
@Pipe({
name: 'timeAgo',
standalone: true,
})
export class TimeAgoPipe implements PipeTransform {
transform(value: string | Date): string {
const date = new Date(value);
const now = new Date();
const seconds = Math.floor((now.getTime() - date.getTime()) / 1000);
if (seconds < 60) return 'just now';
if (seconds < 3600) return `${Math.floor(seconds / 60)} minutes ago`;
if (seconds < 86400) return `${Math.floor(seconds / 3600)} hours ago`;
if (seconds < 2592000) return `${Math.floor(seconds / 86400)} days ago`;
if (seconds < 31536000) return `${Math.floor(seconds / 2592000)} months ago`;
return `${Math.floor(seconds / 31536000)} years ago`;
}
}<p>Posted {{ post.createdAt | timeAgo }}</p>
<!-- "Posted 3 hours ago" -->Filter Pipe
@Pipe({
name: 'filter',
standalone: true,
})
export class FilterPipe implements PipeTransform {
transform<T>(items: T[], field: keyof T, value: string): T[] {
if (!items || !value) return items;
return items.filter(item =>
String(item[field]).toLowerCase().includes(value.toLowerCase())
);
}
}<input [(ngModel)]="searchTerm" placeholder="Filter...">
@for (user of users | filter:'name':searchTerm; track user.id) {
<p>{{ user.name }}</p>
}Pure vs Impure Pipes
| Feature | Pure Pipe (default) | Impure Pipe |
|---|---|---|
| Re-evaluates | Only when input reference changes | Every change detection cycle |
| Performance | ✅ Fast | ⚠️ Can be slow |
| Use case | Stateless transforms | Stateful / side effects |
// Impure pipe — re-evaluates on every change detection
@Pipe({
name: 'sort',
standalone: true,
pure: false, // Makes it impure
})
export class SortPipe implements PipeTransform {
transform(items: any[], field: string): any[] {
return [...items].sort((a, b) => a[field] > b[field] ? 1 : -1);
}
}Best Practice: Prefer pure pipes. If you need impure behavior, consider moving the logic to the component class instead.
Next Steps
With components, templates, directives, and pipes covered, you're ready to learn about services and dependency injection — how Angular manages shared logic and data across components.