Pipes in Angular (built-in and custom)
What are Pipes?
Pipes transform data in Angular templates. They take a value, process it, and return a formatted output. Angular provides many built-in pipes and allows you to create custom ones.
Built-in Pipes
html
<!-- DatePipe -->
<p>{{ today | date }}</p> <!-- Jan 15, 2026 -->
<p>{{ today | date:'fullDate' }}</p> <!-- Thursday, January 15, 2026 -->
<p>{{ today | date:'dd/MM/yyyy' }}</p> <!-- 15/01/2026 -->
<!-- CurrencyPipe -->
<p>{{ price | currency }}</p> <!-- $1,234.50 -->
<p>{{ price | currency:'EUR' }}</p> <!-- €1,234.50 -->
<p>{{ price | currency:'UAH':'symbol' }}</p> <!-- ₴1,234.50 -->
<!-- DecimalPipe -->
<p>{{ 3.14159 | number:'1.2-3' }}</p> <!-- 3.142 -->
<!-- UpperCase / LowerCase / TitleCase -->
<p>{{ 'hello world' | uppercase }}</p> <!-- HELLO WORLD -->
<p>{{ 'HELLO' | lowercase }}</p> <!-- hello -->
<p>{{ 'hello world' | titlecase }}</p> <!-- Hello World -->
<!-- JsonPipe (debugging) -->
<pre>{{ user | json }}</pre>
<!-- SlicePipe -->
<p>{{ 'Angular' | slice:0:3 }}</p> <!-- Ang -->
<!-- AsyncPipe -->
<p>{{ user$ | async }}</p> <!-- Subscribes automatically -->The AsyncPipe
The most important pipe — automatically subscribes and unsubscribes from Observables:
typescript
@Component({
template: \`
<div *ngIf="user$ | async as user">
<h1>{{ user.name }}</h1>
<p>{{ user.email }}</p>
</div>
\`
})
export class UserComponent {
user$ = this.userService.getUser(1);
constructor(private userService: UserService) {}
}Benefits:
- No manual subscribe/unsubscribe — prevents memory leaks
- Automatically triggers change detection
- Works with Observables and Promises
Creating Custom Pipes
typescript
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;
}
}html
<p>{{ longText | truncate }}</p> <!-- 50 chars... -->
<p>{{ longText | truncate:20 }}</p> <!-- 20 chars... -->
<p>{{ longText | truncate:30:'→' }}</p> <!-- 30 chars→ -->Filter Pipe
typescript
@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())
);
}
}html
<input [(ngModel)]="searchTerm">
<div *ngFor="let user of users | filter:'name':searchTerm">
{{ user.name }}
</div>Pure vs Impure Pipes
| Feature | Pure (default) | Impure |
|---|---|---|
| Re-executes when | Input value reference changes | Every change detection cycle |
| Performance | ✅ Efficient | ❌ Can be slow |
| Declaration | @Pipe({ pure: true }) (default) | @Pipe({ pure: false }) |
| Use case | Stateless transforms | Stateful, async, or array mutations |
Chaining Pipes
html
<p>{{ birthday | date:'fullDate' | uppercase }}</p>
<!-- THURSDAY, JANUARY 15, 2026 -->Important:
Pipes transform template data without modifying the source. The AsyncPipe is essential — it handles Observable subscriptions automatically and prevents memory leaks. Create custom pipes for reusable formatting logic. Pipes are pure by default (only re-execute on reference change), which is efficient.
Short Answer
Interview readyPremium
A concise answer to help you respond confidently on this topic during an interview.