← Back to Article List         
Pipes: Standalone Components vs NgModules

Pipes: Standalone Components vs NgModules

Published on 29 Sep 2026     7 min read Angular
Angular Pipes

The main difference is where the pipe is registered/imported.

  • Modern standalone Angular: import the pipe directly into the component.
  • Older NgModule-based Angular: declare custom pipes in an NgModule, then make them available through that module.

1. How Are Pipes Imported into Standalone Components?

What is it?

In a standalone component, the pipes required by that component can be imported directly into the component's imports array.

For example:

@Component({
  imports: [UpperCasePipe]
})

There is no need for an AppModule just to register the pipe.


Why Do We Need It?

Suppose the template contains:

{{ name | uppercase }}

The component needs access to UpperCasePipe.

With standalone components, we explicitly import what the component uses:

imports: [UpperCasePipe]

This makes the component's dependencies clear.


Full Simple Program — Standalone

app.ts

import { Component } from '@angular/core';
import { UpperCasePipe, DatePipe } from '@angular/common';

@Component({
  selector: 'app-root',
  standalone: true,

  imports: [
    UpperCasePipe,
    DatePipe
  ],

  templateUrl: './app.html'
})
export class App {

  name = 'syed mohamed';

  today = new Date();
}

app.html

<h2>Standalone Pipe Example</h2>

<p>
  Name: {{ name | uppercase }}
</p>

<p>
  Date: {{ today | date:'dd/MM/yyyy' }}
</p>

Output:

Name: SYED MOHAMED
Date: 30/09/2026

Explanation

We import the actual pipe classes:

import {
  UpperCasePipe,
  DatePipe
} from '@angular/common';

Then add them to:

imports: [
  UpperCasePipe,
  DatePipe
]

Now the template can use:

{{ name | uppercase }}
{{ today | date:'dd/MM/yyyy' }}

Flow

Standalone Component
        ↓
imports: [UpperCasePipe, DatePipe]
        ↓
Template
        ↓
uppercase / date

Custom Pipe in a Standalone Component

Suppose we have:

gender-pipe.ts

import { Pipe, PipeTransform } from '@angular/core';

@Pipe({
  name: 'gender',
  standalone: true
})
export class GenderPipe implements PipeTransform {

  transform(value: string): string {

    if (value === 'M') {
      return 'Male';
    }

    if (value === 'F') {
      return 'Female';
    }

    return 'Unknown';
  }
}

Then import it directly:

import { Component } from '@angular/core';
import { GenderPipe } from './gender-pipe';

@Component({
  selector: 'app-root',
  standalone: true,

  imports: [
    GenderPipe
  ],

  template: `
    <p>{{ gender | gender }}</p>
  `
})
export class App {

  gender = 'M';
}

Output:

Male

So the important modern approach is:

Pipe
 ↓
Import directly into Component
 ↓
Use in Template

2. How Were Pipes Declared and Imported Using NgModules?

Before standalone components became the preferred approach, Angular applications commonly organized components, directives, and pipes using NgModules.

A custom pipe was generally added to the module's:

declarations: []

Then components belonging to that module could use the pipe.


Full Simple Program — Traditional NgModule

gender.pipe.ts

import { Pipe, PipeTransform } from '@angular/core';

@Pipe({
  name: 'gender'
})
export class GenderPipe implements PipeTransform {

  transform(value: string): string {

    if (value === 'M') {
      return 'Male';
    }

    if (value === 'F') {
      return 'Female';
    }

    return 'Unknown';
  }
}

app.component.ts

import { Component } from '@angular/core';

@Component({
  selector: 'app-root',

  template: `
    <h2>Employee</h2>

    <p>
      Gender: {{ gender | gender }}
    </p>
  `
})
export class AppComponent {

  gender = 'M';
}

app.module.ts

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';

import { AppComponent } from './app.component';
import { GenderPipe } from './gender.pipe';

@NgModule({

  declarations: [
    AppComponent,
    GenderPipe
  ],

  imports: [
    BrowserModule
  ],

  bootstrap: [
    AppComponent
  ]

})
export class AppModule {
}

Explanation

The important part is:

declarations: [
  AppComponent,
  GenderPipe
]

The custom pipe belongs to the AppModule.

Therefore:

AppModule
   │
   ├── AppComponent
   │
   └── GenderPipe

AppComponent can use:

{{ gender | gender }}

because both belong to the same module compilation scope.


What About Built-in Pipes in NgModule Applications?

Built-in pipes such as:

uppercase
lowercase
date
currency
percent

were not manually declared.

For the root browser module, you typically imported:

imports: [
  BrowserModule
]

BrowserModule makes the common Angular directives and pipes available to the root application module.

For feature modules, you commonly imported:

CommonModule

For example:

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';

@NgModule({

  declarations: [
    EmployeeComponent
  ],

  imports: [
    CommonModule
  ]

})
export class EmployeeModule {
}

CommonModule provides common functionality including pipes such as:

DatePipe
UpperCasePipe
LowerCasePipe
CurrencyPipe
PercentPipe

Sharing a Custom Pipe Between NgModules

Suppose:

EmployeeModule
OrderModule
ProductModule

all need the same custom pipe.

Traditionally, we could create a shared module.

shared.module.ts

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';

import { GenderPipe } from './gender.pipe';

@NgModule({

  declarations: [
    GenderPipe
  ],

  imports: [
    CommonModule
  ],

  exports: [
    GenderPipe
  ]

})
export class SharedModule {
}

Then another module imports:

@NgModule({

  declarations: [
    EmployeeComponent
  ],

  imports: [
    CommonModule,
    SharedModule
  ]

})
export class EmployeeModule {
}

The flow becomes:

GenderPipe
    ↓
declared in SharedModule
    ↓
exported from SharedModule
    ↓
EmployeeModule imports SharedModule
    ↓
EmployeeComponent can use GenderPipe

Standalone vs NgModule Approach

Standalone Traditional NgModule
Pipe imported directly into component Custom pipe declared in module
Uses component imports Uses module declarations
No module required for registration NgModule manages compilation scope
Dependencies are explicit per component Dependencies commonly managed at module level
Modern Angular approach Traditional Angular architecture

Standalone

@Component({
  imports: [GenderPipe]
})

Traditional NgModule

@NgModule({
  declarations: [
    AppComponent,
    GenderPipe
  ]
})

Important: declarations vs imports

This is a common interview question.

In traditional NgModule code, a non-standalone custom pipe goes into:

declarations: [
  GenderPipe
]

not:

imports: [
  GenderPipe
]

because declarations was used for the module's own:

Components
Directives
Pipes

Whereas imports was used primarily for other NgModules:

imports: [
  BrowserModule,
  CommonModule,
  SharedModule
]

Modern standalone Angular changes this pattern because standalone pipes can be imported directly:

@Component({
  imports: [
    GenderPipe
  ]
})

Advantages of Standalone Pipe Imports

  • Less NgModule boilerplate.
  • Component dependencies are explicit.
  • Easier to understand which pipe a component uses.
  • Easier composition and reuse.
  • Works naturally with standalone components, directives, and pipes.

Disadvantages

There is some repetition if many standalone components independently import the same pipes:

imports: [
  DatePipe,
  CurrencyPipe,
  UpperCasePipe
]

However, this explicit dependency model is generally intentional and supports clearer dependency boundaries.


Key Points

Modern Standalone

Import pipes directly:

@Component({
  imports: [
    DatePipe,
    CurrencyPipe,
    GenderPipe
  ]
})

Traditional NgModule

Declare your custom pipe:

@NgModule({
  declarations: [
    GenderPipe
  ]
})

For reusable module-based pipes:

Declare
   ↓
Export
   ↓
Import Module
   ↓
Use Pipe

For built-in pipes in traditional feature modules:

imports: [
  CommonModule
]

Interview Questions and Answers

Q1. How do you use a pipe in a standalone component?

Import the standalone pipe directly into the component's imports array.

@Component({
  imports: [DatePipe]
})

Q2. How were custom pipes registered with NgModules?

They were declared in the module's declarations array:

@NgModule({
  declarations: [
    GenderPipe
  ]
})

Q3. Were custom pipes added to imports in traditional NgModules?

A traditional non-standalone pipe was added to:

declarations: []

not directly to:

imports: []

Q4. How did you share a custom pipe between NgModules?

Declare and export it from one module:

@NgModule({
  declarations: [GenderPipe],
  exports: [GenderPipe]
})
export class SharedModule {}

Then import SharedModule wherever the pipe is needed.


Q5. What is the role of CommonModule?

CommonModule provides commonly used Angular directives and pipes to NgModule-based feature modules.

imports: [
  CommonModule
]

Q6. What is the main difference between standalone and NgModule pipe usage?

Standalone:

Pipe
 ↓
Component imports Pipe
 ↓
Template

Traditional NgModule:

Pipe
 ↓
NgModule declares Pipe
 ↓
Component belongs to Module
 ↓
Template

Interview One-Line Answer

In standalone Angular, a pipe can be imported directly into a component's imports array; in traditional NgModule-based Angular, a non-standalone custom pipe was declared in an NgModule and exported/imported through modules when it needed to be shared.