Content Projection in Angular
What is Content Projection?
Content projection lets a parent component pass HTML content into a child component's template. The child uses <ng-content> as a slot where the projected content is rendered. It's Angular's equivalent of "slots" in Vue or "children" in React.
Single-Slot Projection
import { Component } from '@angular/core';
@Component({
selector: 'app-card',
standalone: true,
template: `
<div class="card">
<div class="card-body">
<ng-content />
</div>
</div>
`,
styles: [`.card { border: 1px solid #ddd; border-radius: 8px; padding: 16px; }`]
})
export class CardComponent {}
<!-- Parent template -->
<app-card>
<h2>Card Title</h2>
<p>This content is projected into the card.</p>
<button>Action</button>
</app-card>
Multi-Slot Projection with select
Use the select attribute on <ng-content> to target specific elements by CSS selector.
import { Component } from '@angular/core';
@Component({
selector: 'app-panel',
standalone: true,
template: `
<div class="panel">
<div class="panel-header">
<ng-content select="[panel-header]" />
</div>
<div class="panel-body">
<ng-content select="[panel-body]" />
</div>
<div class="panel-footer">
<ng-content select="[panel-footer]" />
</div>
</div>
`
})
export class PanelComponent {}
<app-panel>
<h2 panel-header>Panel Title</h2>
<p panel-body>Main content goes here.</p>
<button panel-footer>Close</button>
</app-panel>
ng-template and ng-container
<!-- ng-container: grouping without adding DOM elements -->
<ng-container>
@if (isAdmin()) {
<button>Admin Panel</button>
<button>Settings</button>
}
</ng-container>
<!-- ng-template: define a reusable template block -->
<ng-template #loadingTpl>
<div class="spinner">Loading...</div>
</ng-template>
<ng-template #errorTpl let-msg="message">
<div class="error">{{ msg }}</div>
</ng-template>
<!-- Use ngTemplateOutlet to render a template -->
@if (isLoading()) {
<ng-container [ngTemplateOutlet]="loadingTpl" />
}
ContentChild — Accessing Projected Content
import { Component, contentChild, AfterContentInit } from '@angular/core';
import { TabComponent } from './tab.component';
@Component({
selector: 'app-tabs',
standalone: true,
template: `
<div class="tabs">
<ng-content />
</div>
`
})
export class TabsComponent implements AfterContentInit {
// Signal-based contentChild (Angular 17+)
firstTab = contentChild(TabComponent);
ngAfterContentInit() {
// Projected content is available here
console.log('First tab:', this.firstTab());
}
}