Content projection lets a component's consumer supply custom markup that gets rendered inside a predefined slot in the component's template. This lesson covers <ng-content> and multi-slot projection.
What Is Content Projection?
Content projection (similar to "slots" in other frameworks, or children in React) lets a component define a placeholder, <ng-content>, where a parent's markup gets rendered. This makes components like cards, modals, and layout wrappers flexible without hardcoding their inner content.
Without content projection, a card component would have to expose every possible piece of content as a separate @Input(), quickly becoming unwieldy for anything beyond simple text.
// card.component.ts
@Component({
selector: 'app-card',
standalone: true,
template: `
<div class="card">
<ng-content></ng-content>
</div>
`,
})
export class CardComponent {}
<!-- usage -->
<app-card>
<h3>Order #1024</h3>
<p>Shipped on March 3rd.</p>
</app-card>
Everything between <app-card> and </app-card> is projected into the <ng-content> placeholder inside the card's own template.
Content Projection Syntax
<!-- single slot -->
<ng-content></ng-content>
<!-- multi-slot, matched by CSS selector -->
<ng-content select="[card-header]"></ng-content>
<ng-content></ng-content>
<ng-content select="[card-footer]"></ng-content>
A single <ng-content> projects everything a parent places inside the component's tags.
Adding a select attribute creates a named slot that only matches elements with that CSS selector.
An unselected <ng-content> acts as the default slot, catching anything that didn't match a named slot.
Slot matching uses CSS selectors: attribute selectors, class selectors, or element/tag selectors.
Content Projection Cheatsheet
Common patterns for projecting content into reusable components.
Pattern
Example
Purpose
Default slot
<ng-content></ng-content>
Project all unmatched content
Attribute-selected slot
<ng-content select="[card-header]">
Project only elements with that attribute
Class-selected slot
<ng-content select=".footer">
Project only elements with that class
Tag-selected slot
<ng-content select="app-icon">
Project only a specific component/tag
Fallback content
content between the tags (rare)
Angular has no native fallback; use *ngIf/@if with @ContentChild
Multi-Slot Content Projection
For components with multiple distinct content regions (like a card with a header, body, and footer), define multiple <ng-content> elements with select attributes to route content into the right place.
The unmarked <p> falls into the default (unselected) <ng-content> slot in the card body.
Dynamic Projection With ng-template and NgTemplateOutlet
For cases where a parent needs to pass a whole reusable template (potentially with context data), <ng-template> combined with NgTemplateOutlet offers more flexibility than static content projection.
Use @Input() for structured data the component needs to know about (a title string, a price number). Use content projection when the consumer needs to supply arbitrary, possibly complex markup, like a card body, a custom icon, or a list of buttons.
Common Mistakes
Trying to project content into a component that has no <ng-content> placeholder in its template.
Using overly broad select selectors that accidentally capture more elements than intended.
Reaching for many @Input() properties to pass markup piece by piece instead of using content projection.
Forgetting that projected content is compiled in the parent's context, not the child's, so it can't reference the child component's private class members.
Key Takeaways
<ng-content> projects markup supplied by a parent into a component's own template.
Multiple named slots can be created using the select attribute with CSS selectors.
<ng-template> with NgTemplateOutlet supports more advanced, context-aware projection.
Content projection is best for arbitrary markup; inputs are best for structured data.
Pro Tip
Use attribute selectors (like [card-header]) rather than element selectors for named slots, they read clearly in the parent's markup and don't force the parent to use a specific tag name.
You now understand content projection. Next, learn about Dynamic Components for rendering components whose type isn't known until runtime.