Skip to main content

ngTemplateOutlet

What is ngTemplateOutlet?

ngTemplateOutlet is a directive that allows you to render a template dynamically in your component. Instead of duplicating HTML, you can define a template once and reuse it multiple times with different data.

Why Use It?

  • Avoid code duplication - Define template once, use many times
  • Dynamic rendering - Switch between different templates at runtime
  • Reusable components - Let parent components customize child templates
  • Conditional layouts - Show different UI based on conditions
  • List customization - Allow custom item rendering in lists

When to Use It?

Use CaseExample
Reusable UI patternsCards, list items, modals
Conditional layoutsDifferent views for logged in/out users
Customizable componentsTables, lists with custom row templates
Template switchingDifferent layouts based on viewport or user preference
Avoiding duplicationSame HTML structure with different data

Basic Usage

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

@Component({
selector: 'app-template-demo',
standalone: true,
imports: [NgTemplateOutlet],
template: `
<h1>Using ngTemplateOutlet</h1>

<!-- Render the template here -->
<ng-container *ngTemplateOutlet="greetingTemplate"></ng-container>

<!-- Define the template -->
<ng-template #greetingTemplate>
<p>Hello from template!</p>
<p>This can be reused anywhere!</p>
</ng-template>
`
})
export class TemplateDemoComponent {}

Passing Data (Context)

@Component({
selector: 'app-album-template',
standalone: true,
imports: [NgTemplateOutlet],
template: `
<!-- Pass data to template using context -->
<ng-container
*ngTemplateOutlet="albumTemplate; context: {
$implicit: album,
index: 0,
isFeatured: true
}">
</ng-container>

<!-- Template receives data with 'let-' variables -->
<ng-template #albumTemplate let-album let-i="index" let-featured="isFeatured">
<div class="album-card">
<p>Album #{{ i }}: {{ album.name }}</p>
<p>Artist: {{ album.artist }}</p>
<span *ngIf="featured" class="badge">⭐ Featured</span>
</div>
</ng-template>
`
})
export class AlbumTemplateComponent {
album = { name: 'Dark Side of the Moon', artist: 'Pink Floyd' };
}

Reusable Templates

@Component({
selector: 'app-album-cards',
standalone: true,
imports: [NgTemplateOutlet],
template: `
<!-- Use the same template 3 times with different data -->
<ng-container *ngTemplateOutlet="albumTemplate; context: {
name: 'Dark Side of the Moon',
artist: 'Pink Floyd'
}"></ng-container>

<ng-container *ngTemplateOutlet="albumTemplate; context: {
name: 'Abbey Road',
artist: 'The Beatles'
}"></ng-container>

<ng-container *ngTemplateOutlet="albumTemplate; context: {
name: 'Thriller',
artist: 'Michael Jackson'
}"></ng-container>

<!-- Define template once -->
<ng-template #albumTemplate let-name="name" let-artist="artist">
<div class="album-card">
<h2>{{ name }}</h2>
<p>🎤 {{ artist }}</p>
</div>
</ng-template>
`
})
export class AlbumCardsComponent {}

Result: Three album cards with the same structure but different content, without duplicating HTML.

Conditional Templates

@Component({
selector: 'app-player-view',
standalone: true,
imports: [NgTemplateOutlet],
template: `
<button (click)="isPlaying = !isPlaying">Toggle Playback</button>

<!-- Show different template based on condition -->
<ng-container *ngTemplateOutlet="
isPlaying ? playingTemplate : pausedTemplate
"></ng-container>

<ng-template #playingTemplate>
<div class="now-playing">
<h2>🎵 Now Playing: {{ trackTitle }}</h2>
<p>Your playlist is active</p>
<button (click)="pause()">⏸️ Pause</button>
</div>
</ng-template>

<ng-template #pausedTemplate>
<div class="paused">
<h2>⏸️ Playback Paused</h2>
<button (click)="play()">▶️ Resume</button>
</div>
</ng-template>
`
})
export class PlayerViewComponent {
isPlaying = false;
trackTitle = 'Bohemian Rhapsody';

play() { this.isPlaying = true; }
pause() { this.isPlaying = false; }
}

Customizable Components

import { Component, input, TemplateRef } from '@angular/core';
import { NgTemplateOutlet, NgFor } from '@angular/common';

@Component({
selector: 'app-list',
standalone: true,
imports: [NgTemplateOutlet, NgFor],
template: `
<div class="list-container">
<div *ngFor="let item of items(); let i = index" class="list-item">
<!-- Render custom template for each item -->
<ng-container *ngTemplateOutlet="itemTemplate(); context: {
$implicit: item,
index: i
}"></ng-container>
</div>
</div>
`
})
export class ListComponent {
// Signal-based inputs
items = input<any[]>([]);
itemTemplate = input.required<TemplateRef<any>>();
}

Real-World Use Case: Data Table

@Component({
selector: 'app-data-table',
standalone: true,
imports: [NgTemplateOutlet, NgFor],
template: `
<table>
<thead>
<tr>
<th *ngFor="let column of columns()">{{ column.label }}</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let row of data()">
<td *ngFor="let column of columns()">
<!-- Render custom cell template -->
<ng-container *ngTemplateOutlet="
column.cellTemplate;
context: { $implicit: row, column: column }
"></ng-container>
</td>
</tr>
</tbody>
</table>
`
})
export class DataTableComponent {
// Signal-based inputs
columns = input<Column[]>([]);
data = input<any[]>([]);
}

interface Column {
label: string;
cellTemplate: TemplateRef<any>;
}

Best Practices

  • ✅ Use for template reusability - Avoid duplicating HTML
  • ✅ Great for customizable components - Let consumers define how items look
  • ✅ Prefer over complex *ngIf chains - Cleaner than nested conditionals
  • ✅ Use $implicit for primary context value - Simpler syntax
  • ✅ Combine with contentChild() for advanced patterns - More flexible APIs
  • ❌ Don't overuse - Simple components don't need templates
  • ❌ Avoid deep nesting - Keep templates shallow and readable

Common Patterns

PatternUse Case
Single template, multiple rendersCards, alerts, modals
Template switchingDifferent layouts based on state
Parent-provided templatesCustomizable lists, tables, grids
Conditional templatesLogged-in/out views, admin/user UI
Template with loopsDynamic list rendering

ngTemplateOutlet vs Alternatives

ApproachWhen to Use
ngTemplateOutletNeed to reuse or switch templates dynamically
ngIf/ElseSimple show/hide logic (2 options max)
ComponentComplex logic, lifecycle hooks needed
ngSwitchMultiple conditions based on single value