Content Projection
Read one Angular interview question at a time, then flip for the complete explanation, example, and code.
Read one Angular interview question at a time, then flip for the complete explanation, example, and code.
Angular Interview Questions · Content Projection Companion Article
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:
<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.
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:
<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:
<div class="message">
<ng-content></ng-content>
</div>
The final structure behaves conceptually like this:
<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:
<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:
<div class="box">
<ng-content></ng-content>
</div>
Component usage:
<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:
Parent provides content
↓
Component receives content
↓
<ng-content> defines where it appears
Content projection and component inputs solve different problems.
An input is useful when the component requires structured data.
For example:
<app-user-card
[name]="user.name"
[email]="user.email">
</app-user-card>
Content projection is useful when the consumer should provide actual template content.
<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.
The simplest form of content projection is single-slot projection.
A component contains one <ng-content> location.
Example:
<div class="notification">
<ng-content></ng-content>
</div>
Usage:
<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:
Alert
└── Content
Panel
└── Content
Button
└── Button Content
Badge
└── Content
For example, a reusable button could contain:
<button class="primary-button">
<ng-content></ng-content>
</button>
Consumers could then use:
<app-button>Save</app-button>
or:
<app-button>
<span>Save Changes</span>
</app-button>
The component controls the button structure while the consumer controls its inner content.
More complex reusable components often require multiple customizable areas.
For example, a card might contain:
Card
├── Header
├── Body
└── Footer
Angular supports this using multiple <ng-content> elements.
Example:
<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:
<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.
select AttributeThe select attribute determines which projected content should appear inside a particular <ng-content> element.
A selector can target different kinds of content.
<ng-content select="h2"></ng-content>
Usage:
<app-card>
<h2>Angular Tutorial</h2>
</app-card>
The h2 element matches the slot.
<ng-content select=".card-title"></ng-content>
Usage:
<app-card>
<h2 class="card-title">Angular Tutorial</h2>
</app-card>
Attribute-based selectors are often useful for reusable components.
<ng-content select="[cardTitle]"></ng-content>
Usage:
<app-card>
<h2 cardTitle>Angular Tutorial</h2>
</app-card>
This approach clearly communicates the purpose of the projected content.
Another example:
<ng-content select="[actions]"></ng-content>
Consumer:
<div actions>
<button>Save</button>
<button>Cancel</button>
</div>
A component can combine selected slots with a default slot.
<div class="card">
<ng-content select="[cardTitle]"></ng-content>
<div class="card-content">
<ng-content></ng-content>
</div>
</div>
Usage:
<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.
When using multi-slot projection, developers should understand what happens to content that does not match a selector.
Consider:
<ng-content select="[header]"></ng-content>
<ng-content select="[footer]"></ng-content>
And:
<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:
<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:
<ng-content select="[header]"></ng-content>
<ng-content></ng-content>
<ng-content select="[footer]"></ng-content>
Conditional content projection requires some care.
A common mistake is trying to conditionally create the <ng-content> placeholder itself:
@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:
<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>.
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:
<div class="card">
<ng-content select="[cardTitle]">
<h3>Default Title</h3>
</ng-content>
<ng-content>
<p>No content available.</p>
</ng-content>
</div>
Usage:
<app-card></app-card>
Because no matching content was supplied, Angular can display the fallback content.
If the consumer provides content:
<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:
It reduces the amount of configuration required from the component consumer.
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:
contentChild()
and:
contentChildren()
Traditional decorator-based APIs also exist:
@ContentChild()
and:
@ContentChildren()
The key idea is:
View Query
→ Searches the component's own template
Content Query
→ Searches content supplied to the component
This distinction is important.
Suppose the parent uses:
<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.
contentChild()Assume the projected component is:
@Component({
selector: 'app-panel-title',
template: `<h2><ng-content></ng-content></h2>`
})
export class PanelTitleComponent {}
The container component can query for it:
@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.
contentChildren()When multiple projected children may exist, contentChildren() can be used.
Example:
export class TabsComponent {
tabs = contentChildren(TabComponent);
}
Usage:
<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:
Content queries allow a reusable container to do more than display projected content. It can also coordinate the projected components.
Projected content does not need to be plain HTML.
Entire Angular components can be projected.
Example:
<app-dashboard-panel>
<app-user-summary></app-user-summary>
</app-dashboard-panel>
Container template:
<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:
<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.
One of the most important uses of content projection is creating reusable container components.
A container component handles things such as:
The consumer provides the content.
Consider a reusable panel.
<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.
<app-panel>
<h2 panelTitle>User Information</h2>
<p>Name: Amit</p>
<p>Department: Development</p>
<div panelActions>
<button>Edit</button>
</div>
</app-panel>
<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.
Consider a card built only with inputs:
<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:
The component may eventually require many inputs:
title
subtitle
description
image
icon
button1
button2
footer
showImage
showFooter
showButton
...
At that point, content projection may produce a cleaner component API.
For example:
<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.
Inputs and projection are not competing techniques.
A good reusable component often combines them.
Example:
<app-alert type="warning">
Your session will expire in five minutes.
</app-alert>
Here:
type="warning"
is configuration data.
The message is projected content.
The component could use the input to determine styling:
type = input<'success' | 'warning' | 'error'>('success');
while using:
<ng-content></ng-content>
to display arbitrary message content.
This keeps configuration strongly defined while preserving flexibility.
ngProjectAsAngular also provides ngProjectAs for cases where an element should be treated as though it matched another projection selector.
Suppose a component expects:
<ng-content select="[cardTitle]"></ng-content>
Normally the consumer would write:
<h2 cardTitle>Dashboard</h2>
Another option is:
<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.
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:
<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:
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.
Container components should normally focus on styling the layout that surrounds projected content.
Example:
<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:
.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.
A dialog is a good example of multi-slot projection.
Component template:
<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:
<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:
The dialog component does not need to know the specific business meaning of the content.
Avoid creating excessive inputs just to make a layout customizable.
Instead of:
title
description
icon
buttonText
buttonIcon
footerText
consider whether some sections should be projected.
When using selected projection slots, unmatched content needs a default <ng-content> if it should also appear.
<ng-content select="[header]"></ng-content>
<ng-content></ng-content>
<ng-content select="[footer]"></ng-content>
<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:
<div class="content">
<ng-content></ng-content>
</div>
<ng-content>Avoid patterns such as:
@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.
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:
employeeName
employeeSalary
employeeDepartment
A more flexible component could expose generic content areas such as:
title
content
actions
The employee-specific information can then be supplied by the consumer.
Content projection is a strong choice when:
Typical candidates include:
Card
Modal
Dialog
Panel
Alert
Toolbar
Menu
Accordion
Tabs
Form Section
Dashboard Widget
Page Layout
Sidebar Layout
Do not use content projection simply because it is available.
For a component with simple structured data:
<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:
| Requirement | Better Choice |
|---|---|
| Pass a name, ID, state, or configuration | Input |
| Notify parent about an action | Output |
| Insert arbitrary markup | Content projection |
| Provide one flexible content area | Single-slot projection |
| Provide header/body/footer areas | Multi-slot projection |
| Inspect projected children | Content query |
| Dynamically instantiate template content | Template fragments / TemplateRef |
When designing reusable components, keep the projection API simple and predictable.
Good slot names describe purpose:
[cardTitle]
[cardActions]
[dialogTitle]
[dialogActions]
[toolbarStart]
[toolbarEnd]
Avoid selectors that expose unnecessary implementation details.
For example, this is usually clearer:
<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.
Think of content projection as building a reusable frame.
The reusable component defines the frame:
┌────────────────────────┐
│ Header Slot │
├────────────────────────┴
│ │
│ Main Content Slot │
│ │
├────────────────────────┴
│ Action Slot │
└────────────────────────┘
The parent decides what goes into that frame:
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.
<ng-content> defines where projected content is rendered.<ng-content> provides single-slot projection.<ng-content> elements provide multi-slot projection.select attribute routes matching content into specific slots.<ng-content> can act as the default slot for unmatched content.<ng-content>.contentChild() retrieves the first matching projected child.contentChildren() retrieves multiple matching projected children.<ng-content> with Angular template control-flow blocks.