Components & Templates

Angular Pipes

Learn how to use built-in pipes and create custom pipes to transform and format data in Angular templates.

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.

html
{{ 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:

html
<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" -->
FormatOutput 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:

html
<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:

html
<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

html
<p>{{ 0.85 | percent }}</p>
<!-- "85%" -->

<p>{{ 0.8567 | percent:'1.1-1' }}</p>
<!-- "85.7%" -->

UpperCasePipe / LowerCasePipe / TitleCasePipe

html
<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:

html
<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:

html
<pre>{{ user | json }}</pre>
<!--
{
  "name": "John",
  "email": "john@example.com",
  "role": "admin"
}
-->

KeyValuePipe

Iterates over object key-value pairs:

html
@for (entry of config | keyvalue; track entry.key) {
  <p>{{ entry.key }}: {{ entry.value }}</p>
}
typescript
config = {
  theme: 'dark',
  language: 'en',
  notifications: true,
};

AsyncPipe

Subscribes to an Observable or Promise and returns the latest emitted value:

html
<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:

html
<p>{{ birthday | date:'fullDate' | uppercase }}</p>
<!-- "WEDNESDAY, FEBRUARY 18, 2026" -->

<p>{{ user.name | titlecase | slice:0:10 }}</p>

Creating Custom Pipes

Simple Transform Pipe

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;
  }
}

Usage:

html
<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

typescript
@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`;
  }
}
html
<p>Posted {{ post.createdAt | timeAgo }}</p>
<!-- "Posted 3 hours ago" -->

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" placeholder="Filter...">
@for (user of users | filter:'name':searchTerm; track user.id) {
  <p>{{ user.name }}</p>
}

Pure vs Impure Pipes

FeaturePure Pipe (default)Impure Pipe
Re-evaluatesOnly when input reference changesEvery change detection cycle
Performance✅ Fast⚠️ Can be slow
Use caseStateless transformsStateful / side effects
typescript
// 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.