Pipes are a simple and expressive way to transform values directly inside Angular templates. They take a value as input, apply some formatting or transformation, and return the result for display. Pipes are used with the pipe operator |. Instead of changing the original data in the component class, a pipe changes how the value appears in the UI. This makes templates easier to read and keeps presentation-specific formatting logic close to where the value is displayed.
For example, a component may store a raw date object, an unformatted price, a lowercase name, or a long block of text. Pipes let the template decide how those values should be shown to the user. A date can be formatted, a number can be displayed as currency, text can be converted to uppercase, or a long sentence can be shortened. This is one of the reasons pipes are so common in Angular applications: they improve readability while reducing repeated display logic.
currency, date, or uppercase.The general syntax looks like this:
{{ value | pipeName }}
If the pipe needs additional options, those are passed using : after the pipe name.
{{ price | currency:'USD':'symbol':'1.2-2' }}
Multiple pipes can also be chained together. The output of one pipe becomes the input of the next pipe.
{{ name | uppercase | slice:0:5 }}
Angular includes several built-in pipes that solve common presentation needs without extra code. These are often the first pipes beginners use.
| Pipe | Purpose | Example |
|---|---|---|
DatePipe | Formats date values | {{ today | date:'dd/MM/yyyy' }} |
UpperCasePipe | Converts text to uppercase | {{ name | uppercase }} |
LowerCasePipe | Converts text to lowercase | {{ name | lowercase }} |
TitleCasePipe | Capitalizes words | {{ title | titlecase }} |
CurrencyPipe | Formats numbers as currency | {{ price | currency:'USD' }} |
DecimalPipe | Formats decimal precision | {{ pi | number:'1.2-4' }} |
PercentPipe | Formats numbers as percentages | {{ ratio | percent }} |
JsonPipe | Displays objects as JSON | {{ data | json }} |
SlicePipe | Slices strings or arrays | {{ items | slice:0:3 }} |
AsyncPipe | Subscribes to Promises or Observables | {{ user$ | async }} |
KeyValuePipe | Turns object properties into iterable key-value pairs | @for (item of obj | keyvalue; track item.key) |
When using standalone Angular components, built-in pipes usually need to be imported into the component. The example below shows common pipe usage in one place.
import { Component } from '@angular/core';
import {
DatePipe,
CurrencyPipe,
UpperCasePipe,
DecimalPipe,
PercentPipe,
TitleCasePipe,
SlicePipe
} from '@angular/common';
@Component({
selector: 'app-pipes-demo',
standalone: true,
imports: [DatePipe, CurrencyPipe, UpperCasePipe, DecimalPipe, PercentPipe, TitleCasePipe, SlicePipe],
template: `
<p>Today: {{ today | date:'fullDate' }}</p>
<p>Short date: {{ today | date:'dd/MM/yyyy' }}</p>
<p>Name: {{ name | uppercase }}</p>
<p>Title: {{ title | titlecase }}</p>
<p>Price: {{ price | currency:'USD':'symbol':'1.2-2' }}</p>
<p>Pi: {{ pi | number:'1.3-5' }}</p>
<p>Ratio: {{ ratio | percent:'1.0-1' }}</p>
<p>{{ name | uppercase | slice:0:5 }}</p>
`
})
export class PipesDemoComponent {
today = new Date();
name = 'tutorials logic';
title = 'angular pipes introduction';
price = 1234.5;
pi = 3.14159265;
ratio = 0.825;
}
This example shows why pipes are convenient. The raw values stay simple in the component, while the template decides how those values should appear to the user.
DatePipe is one of the most commonly used pipes because applications often need to display dates in user-friendly formats. You can show a full date, short date, month name, time, or a custom pattern. This is useful in dashboards, reports, blog posts, booking systems, and admin panels.
CurrencyPipe, DecimalPipe, and PercentPipe are common in e-commerce, finance, analytics, and reporting. They help make numbers readable without adding formatting logic directly in the component class.
JsonPipe is especially useful while debugging. It should not usually be treated as a final production display tool for users, but it is excellent for inspecting objects during development.
The AsyncPipe deserves special attention because it behaves differently from display-only pipes. It subscribes to an Observable or Promise, returns the latest emitted value, and automatically handles cleanup when the view is destroyed. This makes it extremely useful in Angular applications that rely on RxJS or asynchronous data sources.
import { Component } from '@angular/core';
import { AsyncPipe } from '@angular/common';
import { interval, map } from 'rxjs';
@Component({
selector: 'app-clock',
standalone: true,
imports: [AsyncPipe],
template: `
<p>Seconds passed: {{ seconds$ | async }}</p>
`
})
export class ClockComponent {
seconds$ = interval(1000).pipe(
map(value => value + 1)
);
}
Without AsyncPipe, developers often subscribe manually in the component and then remember to clean up later. The async pipe reduces that boilerplate and keeps the template expressive.
The KeyValuePipe is useful when you need to iterate over object properties in a template. Since templates are often designed around arrays, this pipe helps convert object data into an iterable form.
import { Component } from '@angular/core';
import { KeyValuePipe } from '@angular/common';
@Component({
selector: 'app-settings-view',
standalone: true,
imports: [KeyValuePipe],
template: `
@for (item of settings | keyvalue; track item.key) {
<p>{{ item.key }}: {{ item.value }}</p>
}
`
})
export class SettingsViewComponent {
settings = {
theme: 'dark',
language: 'English',
notifications: true
};
}
When the built-in pipes do not solve your formatting need, you can create a custom pipe. A custom pipe is declared using the @Pipe() decorator and must implement the PipeTransform interface. The main logic goes inside the transform() method.
import { Pipe, PipeTransform } from '@angular/core';
@Pipe({
name: 'truncate',
standalone: true
})
export class TruncatePipe implements PipeTransform {
transform(value: string, limit = 50, ellipsis = '...'): string {
if (!value) return '';
if (value.length <= limit) return value;
return value.substring(0, limit) + ellipsis;
}
}
import { Component } from '@angular/core';
import { TruncatePipe } from './truncate.pipe';
@Component({
selector: 'app-article',
standalone: true,
imports: [TruncatePipe],
template: `
<p>{{ longText | truncate }}</p>
<p>{{ longText | truncate:20 }}</p>
<p>{{ longText | truncate:30:' [read more]' }}</p>
`
})
export class ArticleComponent {
longText = 'Angular is a powerful framework for building modern web applications with TypeScript.';
}
Custom pipes are a good fit for small reusable view transformations such as truncating text, formatting labels, displaying initials, or converting status codes into readable output. If the logic becomes heavy or has side effects, it is usually better placed in a service or component method.
By default, Angular pipes are pure. A pure pipe runs only when Angular detects that the input reference has changed. This makes pure pipes efficient and suitable for most formatting needs. An impure pipe, on the other hand, runs on every change detection cycle. That makes impure pipes more flexible, but also more expensive.
| Type | Runs when | Performance | Typical use |
|---|---|---|---|
| Pure (default) | Input reference changes | Fast | Formatting values for display |
Impure (pure: false) | Every change detection cycle | Slower | Rare special cases with mutable inputs |
Beginners often hear about impure pipes in the context of filtering arrays, but this needs care. In modern Angular, filtering in the template with an impure pipe is usually not the best default choice for performance-sensitive applications.
import { Pipe, PipeTransform } from '@angular/core';
@Pipe({
name: 'filter',
standalone: true,
pure: false
})
export class FilterPipe implements PipeTransform {
transform(items: any[], searchText: string, field: string): any[] {
if (!items || !searchText) return items;
return items.filter(item =>
item[field]?.toLowerCase().includes(searchText.toLowerCase())
);
}
}
This works, but because the pipe runs frequently, it can become expensive in larger lists. It is usually better to compute filtered results in the component or with signals so Angular recalculates only when needed.
In modern Angular, especially with signals, many transformations that might once have been written as impure pipes are now better expressed using computed(). Computed signals only recalculate when their dependencies change, which is more efficient and often easier to reason about.
import { Component, signal, computed } from '@angular/core';
interface User { id: number; name: string; role: string; }
@Component({
selector: 'app-user-list',
standalone: true,
template: `
<input (input)="search.set($any($event.target).value)" placeholder="Search users..." />
<p>Showing {{ filtered().length }} of {{ users().length }} users</p>
@for (user of filtered(); track user.id) {
<div>{{ user.name }} - {{ user.role }}</div>
}
`
})
export class UserListComponent {
search = signal('');
users = signal<User[]>([
{ id: 1, name: 'Alice', role: 'Admin' },
{ id: 2, name: 'Bob', role: 'Developer' },
{ id: 3, name: 'Charlie', role: 'Designer' },
]);
filtered = computed(() =>
this.users().filter(u =>
u.name.toLowerCase().includes(this.search().toLowerCase())
)
);
}
This pattern is often a better fit than an impure filtering pipe because the logic stays explicit and recomputation happens only when the dependent signals actually change.
Use a pipe when the logic is mainly about display formatting. For example, converting text to title case, truncating text, showing a date, formatting a price, or presenting a JSON object for debugging are all good pipe use cases.
Avoid putting heavy business logic, network calls, or mutation-driven behavior inside pipes. A pipe should not become a hidden service or a complicated data-processing layer. If the logic needs external dependencies, has side effects, or is central to application behavior rather than presentation, it usually belongs in the component, a service, or a computed signal instead.
AsyncPipe is only a formatter when it actually manages subscriptions too.Pipes are one of Angular's cleanest features for template-level transformations. They make values easier to display, keep templates expressive, and reduce formatting clutter in components. Built-in pipes handle many common needs immediately, while custom pipes let you create reusable view transformations for your own application. Once you understand when to use pure pipes, when to avoid impure ones, and when a computed signal or service is a better fit, pipes become a very practical part of Angular development.
| operator.
date, currency, uppercase, json, and async cover many common use cases.
@Pipe() and a transform() method.
AsyncPipe is especially important because it subscribes to asynchronous values and handles cleanup automatically.
computed() signals than with impure pipes.
Explore 500+ free tutorials across 20+ languages and frameworks.