Content Projection with ng-content
Letting a parent component pass markup into a child's template with ng-content, Angular's version of slots.
読了時間 2 分
@Input passes data into a component. Sometimes what you want to pass in isn't data at all — it's markup. A generic Card or Modal component shouldn't need to know in advance exactly what will be displayed inside it. Content projection solves this with the <ng-content> tag.
A basic example
@Component({
selector: 'app-card',
template: `
<div class="card">
<ng-content></ng-content>
</div>
`,
styles: [`.card { border: 1px solid #e5e7eb; border-radius: 8px; padding: 1rem; }`],
})
export class CardComponent {}<!-- used by a parent -->
<app-card>
<h3>Angular Signals</h3>
<p>A fine-grained reactivity primitive introduced in recent versions of Angular.</p>
</app-card>Whatever markup a parent places between <app-card> and </app-card> gets rendered wherever <ng-content> appears inside CardComponent's own template. CardComponent never needs to know it's displaying a heading and a paragraph — it just provides the box, and the caller decides what goes in it.
Projecting into multiple slots
A component can define more than one projection point using the select attribute, which matches projected content the way a CSS selector would:
@Component({
selector: 'app-panel',
template: `
<div class="panel">
<header><ng-content select="[panel-title]"></ng-content></header>
<section><ng-content></ng-content></section>
</div>
`,
})
export class PanelComponent {}<app-panel>
<span panel-title>Account Settings</span>
<p>Update your email, password, and notification preferences.</p>
</app-panel><ng-content select="[panel-title]"> only pulls in elements matching that attribute; everything else falls through to the unselected <ng-content> further down. This lets a single component expose a structured layout — a title area and a body area — while still letting the caller supply arbitrary markup for each.
How this compares to @Input
An @Input is right when a component needs a value — a string, a number, an object it can read and use in logic. Content projection is right when a component needs markup — HTML that should render as-is, possibly with its own nested components, event bindings, and structure that the container component has no reason to understand. Trying to pass a whole chunk of HTML through a string @Input and rendering it with innerHTML throws away everything Angular does for you (change detection, event binding, security sanitization) — <ng-content> is the mechanism designed for exactly this case.
Reusable wrapper components — cards, modals, tabs, accordions, layout shells — are the most common place content projection shows up, because their whole purpose is providing structure and behavior around content they don't own.