Content Projection

Read one Angular interview question at a time, then flip for the complete explanation, example, and code.

0/60 Known filtered set
Level
Read Full Guide Question: 1 of 60

Angular Interview Questions · Content Projection Companion Article

Content Projection

Angular applications are built by combining many small components. Sometimes a component should control its layout and appearance while allowing another component to decide what content appears inside that layout. This requirement is handled through content projection.

Content projection allows a parent component to pass HTML, text, components, buttons, forms, images, or other template content into predefined locations inside another component. A common example is a reusable card:

HTML
<app-card>
  <h2>Angular Course</h2>
  <p>Learn Angular from basic to advanced concepts.</p>
</app-card>

The app-card component controls the card design, while the parent provides the actual heading and description. This makes components more flexible because the reusable component does not need to know every possible type of content that might appear inside it.

What Is Content Projection?

Content projection is the Angular mechanism used to place content supplied by a component's consumer into the template of that component.

Consider this component usage:

HTML
<app-message>
  <p>Your profile has been updated successfully.</p>
</app-message>

The paragraph is written outside the app-message template.

However, the message component can display it inside its own layout:

HTML
<div class="message">
  <ng-content></ng-content>
</div>

The final structure behaves conceptually like this:

HTML
<div class="message">
  <p>Your profile has been updated successfully.</p>
</div>

The paragraph is called projected content.

Content projection is especially useful when creating:

  • cards
  • dialogs
  • modals
  • panels
  • alerts
  • reusable buttons
  • form containers
  • page layouts
  • tabs
  • toolbars
  • reusable UI libraries

<ng-content>

<ng-content> is the main Angular element used for content projection.

It represents a location inside a component template where content provided by the consumer should appear.

Example component template:

HTML
<div class="box">
  <ng-content></ng-content>
</div>

Component usage:

HTML
<app-box>
  <h3>Important Information</h3>
  <p>Please complete your profile before continuing.</p>
</app-box>

Both elements are projected into the <ng-content> location.

The important point is that <ng-content> is not a normal HTML element. Angular processes it as a special content projection placeholder.

You should therefore think of it as:

Text
Parent provides content
        ↓
Component receives content
        ↓
<ng-content> defines where it appears

Content Projection vs Component Inputs

Content projection and component inputs solve different problems.

An input is useful when the component requires structured data.

For example:

HTML
<app-user-card
  [name]="user.name"
  [email]="user.email">
</app-user-card>

Content projection is useful when the consumer should provide actual template content.

HTML
<app-user-card>
  <h2>{{ user.name }}</h2>
  <p>{{ user.email }}</p>
  <button>Edit Profile</button>
</app-user-card>

Use inputs when the reusable component needs data.

Use content projection when the reusable component needs flexible markup.

In real applications, components frequently use both techniques together.

Single-Slot Projection

The simplest form of content projection is single-slot projection.

A component contains one <ng-content> location.

Example:

HTML
<div class="notification">
  <ng-content></ng-content>
</div>

Usage:

HTML
<app-notification>
  <strong>Success!</strong>
  <span>Your changes have been saved.</span>
</app-notification>

Everything placed inside <app-notification> is projected into the same location.

Single-slot projection works well when the reusable component only needs one customizable content area.

Typical examples include:

Text
Alert
 └── Content

Panel
 └── Content

Button
 └── Button Content

Badge
 └── Content

For example, a reusable button could contain:

HTML
<button class="primary-button">
  <ng-content></ng-content>
</button>

Consumers could then use:

HTML
<app-button>Save</app-button>

or:

HTML
<app-button>
  <span>Save Changes</span>
</app-button>

The component controls the button structure while the consumer controls its inner content.

Multi-Slot Projection

More complex reusable components often require multiple customizable areas.

For example, a card might contain:

Text
Card
├── Header
├── Body
└── Footer

Angular supports this using multiple <ng-content> elements.

Example:

HTML
<div class="card">
  <header>
    <ng-content select="[cardHeader]"></ng-content>
  </header>

  <main>
    <ng-content select="[cardBody]"></ng-content>
  </main>

  <footer>
    <ng-content select="[cardFooter]"></ng-content>
  </footer>
</div>

The consumer can provide content for each slot:

HTML
<app-card>
  <h2 cardHeader>User Profile</h2>

  <div cardBody>
    <p>Name: Rahul</p>
    <p>Role: Developer</p>
  </div>

  <div cardFooter>
    <button>Edit</button>
  </div>
</app-card>

Angular matches each projected element with the appropriate <ng-content> location.

This allows reusable components to provide a fixed structure without forcing fixed content.

The select Attribute

The select attribute determines which projected content should appear inside a particular <ng-content> element.

A selector can target different kinds of content.

Selecting an Element

HTML
<ng-content select="h2"></ng-content>

Usage:

HTML
<app-card>
  <h2>Angular Tutorial</h2>
</app-card>

The h2 element matches the slot.

Selecting by CSS Class

HTML
<ng-content select=".card-title"></ng-content>

Usage:

HTML
<app-card>
  <h2 class="card-title">Angular Tutorial</h2>
</app-card>

Selecting by Attribute

Attribute-based selectors are often useful for reusable components.

HTML
<ng-content select="[cardTitle]"></ng-content>

Usage:

HTML
<app-card>
  <h2 cardTitle>Angular Tutorial</h2>
</app-card>

This approach clearly communicates the purpose of the projected content.

Another example:

HTML
<ng-content select="[actions]"></ng-content>

Consumer:

HTML
<div actions>
  <button>Save</button>
  <button>Cancel</button>
</div>

Default Projection Slot

A component can combine selected slots with a default slot.

HTML
<div class="card">
  <ng-content select="[cardTitle]"></ng-content>

  <div class="card-content">
    <ng-content></ng-content>
  </div>
</div>

Usage:

HTML
<app-card>
  <h2 cardTitle>Account Settings</h2>

  <p>Manage your personal information here.</p>
  <button>Update Profile</button>
</app-card>

The element containing cardTitle goes into the first slot.

Everything that does not match that selector can be captured by the unselected <ng-content>.

This is useful when only certain parts of a component require special positioning.

Unmatched Projected Content

When using multi-slot projection, developers should understand what happens to content that does not match a selector.

Consider:

HTML
<ng-content select="[header]"></ng-content>
<ng-content select="[footer]"></ng-content>

And:

HTML
<app-layout>
  <h2 header>Dashboard</h2>

  <p>Dashboard information</p>

  <button footer>Close</button>
</app-layout>

The paragraph does not match [header] or [footer].

If the component does not contain a general:

HTML
<ng-content></ng-content>

there is no default projection location for that paragraph.

A useful reusable component often includes a catch-all slot when arbitrary body content should be supported:

HTML
<ng-content select="[header]"></ng-content>
<ng-content></ng-content>
<ng-content select="[footer]"></ng-content>

Conditional Content

Conditional content projection requires some care.

A common mistake is trying to conditionally create the <ng-content> placeholder itself:

HTML
@if (showContent) {
  <ng-content></ng-content>
}

This should generally be avoided.

Angular processes <ng-content> placeholders as part of the component template at build time. They are not dynamic containers that should be created and destroyed using template control flow.

If the parent controls whether content exists, place the condition around the content being supplied.

Example:

HTML
<app-card>
  @if (showDetails) {
    <p>Additional account details</p>
  }
</app-card>

Here the parent decides whether the projected paragraph should exist.

For situations where the receiving component itself must dynamically control when supplied template content is created, template fragments such as ng-template and TemplateRef are usually more appropriate than attempting to conditionally create <ng-content>.

Fallback Content

Sometimes a component should display default content when the consumer does not provide anything for a particular slot.

Angular supports fallback content directly inside <ng-content>.

Example:

HTML
<div class="card">
  <ng-content select="[cardTitle]">
    <h3>Default Title</h3>
  </ng-content>

  <ng-content>
    <p>No content available.</p>
  </ng-content>
</div>

Usage:

HTML
<app-card></app-card>

Because no matching content was supplied, Angular can display the fallback content.

If the consumer provides content:

HTML
<app-card>
  <h2 cardTitle>Order Details</h2>
  <p>Order number: 1025</p>
</app-card>

the supplied content replaces the corresponding fallback content.

Fallback projection is useful for:

  • default titles
  • empty messages
  • optional descriptions
  • default button labels
  • placeholder text
  • reusable empty states

It reduces the amount of configuration required from the component consumer.

Content Queries

Sometimes projecting content is not enough.

The receiving component may also need to obtain a reference to a projected component, directive, template, or element.

Angular provides content queries for this purpose.

Modern Angular provides signal-based query APIs including:

TypeScript
contentChild()

and:

TypeScript
contentChildren()

Traditional decorator-based APIs also exist:

TypeScript
@ContentChild()

and:

TypeScript
@ContentChildren()

The key idea is:

Text
View Query
→ Searches the component's own template

Content Query
→ Searches content supplied to the component

This distinction is important.

Suppose the parent uses:

HTML
<app-panel>
  <app-panel-action>Save</app-panel-action>
</app-panel>

The app-panel-action instance was supplied by the parent.

Therefore, app-panel can inspect it using a content query.

Using contentChild()

Assume the projected component is:

TypeScript
@Component({
  selector: 'app-panel-title',
  template: `<h2><ng-content></ng-content></h2>`
})
export class PanelTitleComponent {}

The container component can query for it:

TypeScript
@Component({
  selector: 'app-panel',
  template: `
    <section>
      <ng-content></ng-content>
    </section>
  `
})
export class PanelComponent {
  title = contentChild(PanelTitleComponent);
}

The query returns a signal representing the first matching projected child.

It can be useful when a container needs to determine whether a particular projected feature exists.

Using contentChildren()

When multiple projected children may exist, contentChildren() can be used.

Example:

TypeScript
export class TabsComponent {
  tabs = contentChildren(TabComponent);
}

Usage:

HTML
<app-tabs>
  <app-tab>Profile</app-tab>
  <app-tab>Security</app-tab>
  <app-tab>Notifications</app-tab>
</app-tabs>

The container can access the collection of matching projected tab components.

This pattern is useful for components such as:

  • tabs
  • menus
  • accordions
  • step indicators
  • navigation groups
  • toolbars
  • form groups

Content queries allow a reusable container to do more than display projected content. It can also coordinate the projected components.

Content Projection with Components

Projected content does not need to be plain HTML.

Entire Angular components can be projected.

Example:

HTML
<app-dashboard-panel>
  <app-user-summary></app-user-summary>
</app-dashboard-panel>

Container template:

HTML
<section class="dashboard-panel">
  <ng-content></ng-content>
</section>

This makes content projection useful for component composition.

Instead of building one very large component with dozens of configuration options, developers can combine smaller components together.

For example:

HTML
<app-dialog>
  <app-dialog-header>
    Delete Account
  </app-dialog-header>

  <app-dialog-body>
    Are you sure you want to delete your account?
  </app-dialog-body>

  <app-dialog-actions>
    <button>Cancel</button>
    <button>Delete</button>
  </app-dialog-actions>
</app-dialog>

The resulting API is readable and closely represents the visual structure of the interface.

Reusable Container Components

One of the most important uses of content projection is creating reusable container components.

A container component handles things such as:

  • layout
  • borders
  • spacing
  • background
  • responsive behavior
  • accessibility structure
  • common actions
  • shared styling

The consumer provides the content.

Consider a reusable panel.

panel.component.html

HTML
<section class="panel">
  <div class="panel-header">
    <ng-content select="[panelTitle]"></ng-content>
  </div>

  <div class="panel-body">
    <ng-content></ng-content>
  </div>

  <div class="panel-actions">
    <ng-content select="[panelActions]"></ng-content>
  </div>
</section>

The same component can now serve completely different purposes.

User Panel

HTML
<app-panel>
  <h2 panelTitle>User Information</h2>

  <p>Name: Amit</p>
  <p>Department: Development</p>

  <div panelActions>
    <button>Edit</button>
  </div>
</app-panel>

Product Panel

HTML
<app-panel>
  <h2 panelTitle>Product Details</h2>

  <p>Angular Interview Course</p>
  <p>Access: Free</p>

  <div panelActions>
    <button>Start Learning</button>
  </div>
</app-panel>

No additional panel component was required.

The layout remains consistent while the content changes.

Content Projection vs Hard-Coded Components

Consider a card built only with inputs:

HTML
<app-card
  title="Angular"
  description="Learn Angular"
  buttonText="Start">
</app-card>

This works when the card has a predictable structure.

Problems begin when different cards need:

  • two buttons
  • an image
  • formatted text
  • another component
  • a list
  • a form
  • icons
  • custom HTML

The component may eventually require many inputs:

Text
title
subtitle
description
image
icon
button1
button2
footer
showImage
showFooter
showButton
...

At that point, content projection may produce a cleaner component API.

For example:

HTML
<app-card>
  <img src="angular.png" alt="Angular">

  <h2 cardTitle>Angular</h2>

  <p>Learn modern Angular development.</p>

  <div cardActions>
    <button>Learn</button>
    <button>Practice</button>
  </div>
</app-card>

The card controls layout while the consumer controls content composition.

Combining Inputs and Content Projection

Inputs and projection are not competing techniques.

A good reusable component often combines them.

Example:

HTML
<app-alert type="warning">
  Your session will expire in five minutes.
</app-alert>

Here:

Text
type="warning"

is configuration data.

The message is projected content.

The component could use the input to determine styling:

TypeScript
type = input<'success' | 'warning' | 'error'>('success');

while using:

HTML
<ng-content></ng-content>

to display arbitrary message content.

This keeps configuration strongly defined while preserving flexibility.

ngProjectAs

Angular also provides ngProjectAs for cases where an element should be treated as though it matched another projection selector.

Suppose a component expects:

HTML
<ng-content select="[cardTitle]"></ng-content>

Normally the consumer would write:

HTML
<h2 cardTitle>Dashboard</h2>

Another option is:

HTML
<h2 ngProjectAs="[cardTitle]">Dashboard</h2>

Angular treats that element as matching the specified projection selector.

This is an advanced feature and is not needed for most basic content projection scenarios, but it can be useful when building flexible component APIs.

The ngProjectAs value is intended to be static rather than dynamically bound.

Projected Content and Component Ownership

One important concept is that projected content visually appears inside the receiving component but still belongs to the component that originally declared it.

For example:

HTML
<app-card>
  <p>{{ username }}</p>
</app-card>

If username belongs to the parent component, the expression still uses the parent's context.

The card does not need its own username property.

This behavior is powerful because projected templates can continue using the data and expressions available where they were originally written.

Conceptually:

Text
Parent Component
│
├── owns username
│
└── supplies <p>{{ username }}</p>
          │
          ▼
      app-card
          │
          ▼
     <ng-content>

The content changes its visual location, but it does not suddenly become part of the card component's own template context.

Styling Projected Content

Container components should normally focus on styling the layout that surrounds projected content.

Example:

HTML
<div class="card">
  <div class="card-header">
    <ng-content select="[cardTitle]"></ng-content>
  </div>

  <div class="card-body">
    <ng-content></ng-content>
  </div>
</div>

CSS:

CSS
.card {
  border: 1px solid #ddd;
  border-radius: 8px;
}

.card-header {
  padding: 16px;
  font-weight: 600;
}

.card-body {
  padding: 16px;
}

The component controls the structure and spacing of its slots.

This approach usually produces a cleaner reusable API than requiring the container to know the exact internal HTML structure of every piece of projected content.

Practical Dialog Example

A dialog is a good example of multi-slot projection.

Component template:

HTML
<div class="dialog">
  <header class="dialog-header">
    <ng-content select="[dialogTitle]">
      <h2>Information</h2>
    </ng-content>
  </header>

  <div class="dialog-content">
    <ng-content></ng-content>
  </div>

  <footer class="dialog-actions">
    <ng-content select="[dialogActions]">
      <button>Close</button>
    </ng-content>
  </footer>
</div>

Usage:

HTML
<app-dialog>
  <h2 dialogTitle>Delete Record</h2>

  <p>
    This operation permanently deletes the selected record.
  </p>

  <div dialogActions>
    <button>Cancel</button>
    <button>Delete</button>
  </div>
</app-dialog>

This component provides:

  • a reusable layout
  • customizable title
  • customizable body
  • customizable buttons
  • sensible fallback content

The dialog component does not need to know the specific business meaning of the content.

Common Content Projection Mistakes

1. Using Inputs for Every Piece of HTML

Avoid creating excessive inputs just to make a layout customizable.

Instead of:

Text
title
description
icon
buttonText
buttonIcon
footerText

consider whether some sections should be projected.

2. Forgetting the Default Slot

When using selected projection slots, unmatched content needs a default <ng-content> if it should also appear.

HTML
<ng-content select="[header]"></ng-content>
<ng-content></ng-content>
<ng-content select="[footer]"></ng-content>

3. Trying to Treat <ng-content> Like a Normal DOM Element

<ng-content> is an Angular projection placeholder rather than an actual rendered HTML element.

Do not design application logic as though you can manipulate it like a normal <div>.

Use surrounding HTML elements when you need classes, layout, styling, or DOM behavior.

Instead of trying to style the placeholder itself, use:

HTML
<div class="content">
  <ng-content></ng-content>
</div>

4. Conditionally Creating <ng-content>

Avoid patterns such as:

HTML
@if (visible) {
  <ng-content></ng-content>
}

If the projected content itself should be conditional, the condition can often be handled by the consumer.

For more advanced dynamic rendering controlled by the receiving component, template fragments provide greater control.

5. Making a Container Too Specific

A reusable container should not become unnecessarily dependent on one business use case.

For example, a general card should not necessarily require properties such as:

Text
employeeName
employeeSalary
employeeDepartment

A more flexible component could expose generic content areas such as:

Text
title
content
actions

The employee-specific information can then be supplied by the consumer.

When to Use Content Projection

Content projection is a strong choice when:

  • the component acts as a visual container
  • consumers need to provide arbitrary HTML
  • different consumers require different content structures
  • multiple configurable content regions are required
  • child components need to be composed together
  • creating dozens of presentation-related inputs would make the API complicated

Typical candidates include:

Text
Card
Modal
Dialog
Panel
Alert
Toolbar
Menu
Accordion
Tabs
Form Section
Dashboard Widget
Page Layout
Sidebar Layout

When Content Projection May Not Be Necessary

Do not use content projection simply because it is available.

For a component with simple structured data:

HTML
<app-avatar
  [name]="user.name"
  [image]="user.image">
</app-avatar>

inputs may be easier to understand.

Similarly, if the component itself must completely control the exact DOM structure, allowing arbitrary projected markup might make the component harder to maintain.

Choose the technique based on component responsibility:

RequirementBetter Choice
Pass a name, ID, state, or configurationInput
Notify parent about an actionOutput
Insert arbitrary markupContent projection
Provide one flexible content areaSingle-slot projection
Provide header/body/footer areasMulti-slot projection
Inspect projected childrenContent query
Dynamically instantiate template contentTemplate fragments / TemplateRef

Content Projection Design Guidelines

When designing reusable components, keep the projection API simple and predictable.

Good slot names describe purpose:

HTML
[cardTitle]
[cardActions]
[dialogTitle]
[dialogActions]
[toolbarStart]
[toolbarEnd]

Avoid selectors that expose unnecessary implementation details.

For example, this is usually clearer:

HTML
<ng-content select="[cardTitle]"></ng-content>

than requiring consumers to understand an internal CSS class name.

Also avoid creating too many slots unless they provide a clear benefit.

A component with fifteen different projection locations can become just as difficult to understand as a component with fifteen inputs.

A Useful Mental Model

Think of content projection as building a reusable frame.

The reusable component defines the frame:

Text
┌────────────────────────┐
│ Header Slot             │
├────────────────────────┴
│                         │
│ Main Content Slot       │
│                         │
├────────────────────────┴
│ Action Slot             │
└────────────────────────┘

The parent decides what goes into that frame:

Text
Header  → "Delete Account"

Content → Warning message

Actions → Cancel | Delete

The container owns the structure.

The consumer owns the content.

This separation is the main reason content projection is valuable when building reusable Angular UI components.

Key Points to Remember

  • Content projection allows a component to receive template content from its consumer.
  • <ng-content> defines where projected content is rendered.
  • One <ng-content> provides single-slot projection.
  • Multiple <ng-content> elements provide multi-slot projection.
  • The select attribute routes matching content into specific slots.
  • Attribute selectors are useful for clearly named projection areas.
  • An unselected <ng-content> can act as the default slot for unmatched content.
  • Fallback content can be placed inside <ng-content>.
  • Content queries allow a component to inspect projected children.
  • contentChild() retrieves the first matching projected child.
  • contentChildren() retrieves multiple matching projected children.
  • Projected content remains associated with the context in which it was declared.
  • Inputs and content projection can be combined in the same component.
  • Avoid conditionally creating <ng-content> with Angular template control-flow blocks.
  • Use content projection primarily when component consumers need control over markup rather than only data.

Question Hint