← Back to all tutorials

Ng-Content Directive

Use ng-content for content projection — pass HTML content from a parent component into a child component's template.

Ng-Content Directive

<ng-content> allows you to project content from a parent component into a child component. Think of it as a "slot" where parent HTML gets inserted.

The Problem

<!-- Parent template -->
<app-card>
    <h3>This content is IGNORED!</h3>
    <p>Nothing between the tags shows up.</p>
</app-card>

<!-- Card template -->
<div class="card">
    <!-- Where does the parent content go? Nowhere! -->
</div>

The Solution: ng-content

<!-- card.component.html -->
<div class="card">
    <ng-content></ng-content>  <!-- Parent content is projected HERE -->
</div>
<!-- Parent template -->
<app-card>
    <h3>Card Title</h3>
    <p>This content now appears inside the card!</p>
</app-card>

<!-- Rendered result: -->
<div class="card">
    <h3>Card Title</h3>
    <p>This content now appears inside the card!</p>
</div>

Reusable Card Component

// card.component.ts
@Component({
    selector: 'app-card',
    template: `
        <div class="card">
            <ng-content></ng-content>
        </div>
    `,
    styles: [`
        .card {
            background: white;
            border-radius: 12px;
            padding: 24px;
            box-shadow: 0 2px 12px rgba(0,0,0,0.08);
            margin-bottom: 20px;
        }
    `]
})
<!-- Now use it with ANY content: -->
<app-card>
    <h3>User Profile</h3>
    <p>Name: Alice Johnson</p>
</app-card>

<app-card>
    <h3>Statistics</h3>
    <ul>
        <li>Posts: 42</li>
        <li>Followers: 1,200</li>
    </ul>
</app-card>

Multi-Slot Projection

Use the select attribute to project content into specific slots:

<!-- panel.component.html -->
<div class="panel">
    <div class="panel-header">
        <ng-content select="[panel-header]"></ng-content>
    </div>
    <div class="panel-body">
        <ng-content select="[panel-body]"></ng-content>
    </div>
    <div class="panel-footer">
        <ng-content select="[panel-footer]"></ng-content>
    </div>
</div>
<!-- Parent usage: -->
<app-panel>
    <div panel-header>
        <h2>Settings</h2>
    </div>
    <div panel-body>
        <p>Configure your preferences here.</p>
    </div>
    <div panel-footer>
        <button>Save</button>
    </div>
</app-panel>

Select Options

SelectorMatches
select="h2"Element: <h2>
select=".header"Class: <div class="header">
select="[panel-header]"Attribute: <div panel-header>
select="app-title"Component: <app-title>

Default Slot (Catch-All)

<!-- A ng-content without select catches everything else: -->
<div class="card">
    <div class="card-header">
        <ng-content select="[card-title]"></ng-content>
    </div>
    <div class="card-body">
        <ng-content></ng-content>  <!-- Everything else goes here -->
    </div>
</div>

Key Takeaways

  • <ng-content> creates a slot where parent HTML is projected into the child
  • Without ng-content, content between component tags is discarded
  • Use select for multi-slot projection — target by element, class, or attribute
  • A plain <ng-content> without select is the default catch-all slot
  • Content projection is essential for building reusable wrapper components