Here's what you should know when creating flexible and reusable components in Angular
In this article we're going to explore the difference between ng-content and ng-template and see when to use which

Reusable components appear in almost every modern web application. Not only do we have to reuse the same components in complex business applications, we also have to reuse them in a different context.
When reusing a component in a different business context it's important that a part of the component is flexible and can be adjusted to the use case.
Imagine tabs, cards or an expander. All those elements have a similar base structure and behaviour but the content can differ. We need a way to integrate a flexible content with a reusable base.
Modern web technologies and especially Angular offer us great mechanisms to implement such components.
Templates and slots
The base idea of reusable and flexible components is simple. You have a reusable host component. This component contains some standard behaviour and template which is always the same.
Furthermore it also defines some slots. Those slots define where the flexible content will end up. Depending on the use case, different content can be passed to the same component.

In order to create flexible web components, modern browsers support the <slot>
and <template>
tag.
The <template>
tag holds content but will not render anything on the page.
<template id="foo">
<style>
h4 { color: blue }
</style>
<h4><slot name="title"></slot></h4>
</template>
To render the content of a <template>
tag to the DOM we need to write some lines of JavaScript. In the example above we will register our template as a customElement
.
customElements.define('foo',
class extends HTMLElement {
constructor() {
super();
let template = document.getElementById('foo');
let templateContent = template.content;
const shadowRoot = this.attachShadow({mode: 'open'})
.appendChild(templateContent.cloneNode(true));
}
}
);
Once our web component is registered we can use it and pass some dynamic content to it.
<foo>
<span slot="title">Title of the foo web component</span>
</foo>
To pass the dynamic content we need to define it inside a tag with the slot
attribute on it. The value of the slot
attribute decides where the dynamic content will end up.
The foo
component can be reused in various scenarios and its content can differ from use case to use case.
Components with dynamic content in Angular
Angular offers us similar concepts to handle dynamic content; ng-content
and ng-template
.
Ng-template
ng-template
has the ability to hold content without rendering it. It is the underlying mechanism to all directives that modify the DOM structure and therefore a fundamental concept behind all structural Angular directives like *ngIf
or *ngFor
.
ng-template
is very powerful and can be used for multiple things, one of them is creating flexible components by passing a template to a host component.
Let's have a look at a simple heading
component that accepts any kind of title. Could be an h1
, h2
or any other tag.
First we prepare the dynamic content so that we can hand it over to the host component.
<ng-template #theTruth>
<h4>Real Madrid - best football club ever</h4>
</ng-template>
Since this code is wrapped in an ng-template
it doesn't render anything. The beautiful thing is that we can use a template reference (#theTruth
) to access this template, pass it around and use it wherever we want.
So if we want to display this title inside a heading
component, we could pass the template as an @Input
property and then display it.
<ng-template #theTruth>
<h4>Real Madrid - best football club ever</h4>
</ng-template>
<heading [title]="theTruth"> </heading>
Inside our heading component we could use *ngTemplateOutlet
in combination with ng-container
to render it.
<!-- some other HTML -->
<ng-container *ngTemplateOutlet="title"></ng-container>
This is one way of creating flexible components in Angular. Another and more convenient way is to use ng-content
.
Ng-content
ng-content
is similar to the slot
tag. It allows us to specify spots in the host components. Those spots define where the dynamic content will end up. When we use ng-content
we usually speak of content projection.
A host component can define multiple slots. If it does, it usually uses the select
property with a selector on ng-content
to decide which content should end up in the slot. If no selector matches, a projected content ends up in the ng-content
slot without a selector.
Therefore the template of the heading
component is very simple. It has some HTML and accepts some content which will be projected to the default slot.
<!-- some other HTML -->
<ng-content></ng-content>
The heading component could then be used in the following way.
<heading>
<h4>Real Madrid - best football club ever</h4>
</heading>
We have now seen the two approaches Angular offers us to build flexible components. The question now is, which one to use and is there a difference in the approaches? Let’s answer this question by implementing a real use case in the form of an expander
component.
Creating a flexible "expander" component
A classic expander
consists of a header, an expand icon, and a content. While the header is just a text that differs, the content can be any kind of HTML element, an Angular component, or even plain text. It varies from use case to use case.

This sounds like a good match for ng-content
.
Implementing an expander with ng-content
The ng-content
tag is used in the host component, the component that accepts the flexible content. It acts as a placeholder for the projected content and allows us to specify where the projected content will end up.
Let’s take a closer look by implementing an expander from scratch.

Our div with the header
class displays the value of the heading
property received via @Input
. The ng-content
tag specifies where our content is projected. Clicking on the header allows us to expand the expander (Show or hide the projected content).
This expander
component can then be used in the following way.

Projecting a component
Currently, we project text to our expander, which is not very interesting. Let’s take it a step further and project a clock
component that displays the current time.
@Component({
selector: 'clock',
template: `{{currentTime}}`,
styleUrls: ['./clock.component.scss']
})
export class ClockComponent implements OnInit {
currentTime: string;
ngOnInit() {
const date = new Date();
this.currentTime = `${date.getHours()}:${date.getMinutes()}`
}
}
The clock
component is straightforward. Inside the ngOnInit
lifecycle hook, we get the hours and the minutes of the present time and assign it to a variable which we then display in the template.
Now, it’s time to show this component in our expander.
<expander heading="Expand to see the current time">
<clock></clock>
</expander>
Which then nicely displays the time.

But, what happens if you close and reopen the expander after 5 minutes? It still displays 9:51
, why is that? ?
Ng-content and lifecycle hooks
The source of the problem lies in the way ng-content
calls life cycle hooks. Let’s inspect this further by logging out the ngOnInit
and the ngOnDestroy
lifecycle hooks of our clock
component.
Let’s open up the dev tools and refresh our application.

Even though our clock component isn’t yet rendered (the ngIf
expression of the expander is stillfalse
) the ngOnInit
lifecycle hook is already called.
Let’s now expand our component without clearing the log.

We can see that the ngOnInit
lifecycle hook is not called again, and we, therefore, don’t get the current time, but the time we started our application.
Gosh! But there’s even more to it. So far we only looked at the ngOnInit
hook, what about ngOnDestroy
? Let’s close the expander again without clearing the log.

No ngOnDestroy
call happened even though the component is completely removed from the DOM.
When are the ngOnInit and the ngOnDestroy hooks called?
When projecting content via ng-content
the life cycle hooks are bound to the life cycle of the parent component.
Let’s inspect this by adding a new button to toggle our expander
component.

We can see that every time our expander is rendered or removed, the life cycle hooks of our projected content is called.
The lifecycle hooks of the projected content are bound to the lifecycle of the host.
Let’s summarize:
ngOnInit
is called once the host is renderedngOnDestroy
is called once the host is destroyed- Removing and rendering the projected content via
ngIf
doesn’t call the life cycles.
When is this behavior problematic?
This behavior can become very problematic once the projected component does some heavy logic or position calculation in the ngOnInit
life cycle hook.
Furthermore, since we work with RxJS, chances are there that this behavior can lead to memory leaks. The ngOnDestroy
hook may never get called, and therefore, streams are never properly unsubscribed. Unhandled subscriptions can easily result in memory leaks once your application grows.
If you want to find out more about subscription managment and memory leaks I highly recommend you to check out this post How to create a memory leak in Angular
How to solve this issue?
One way of solving this issue is to change the approach from ng-content
to ng-template
. Let’s refactor our expander component to take advantage of ng-template
.
First, we add a new @Input
that accepts a TemplatRef
, which is our content.
@Input() content: TemplateRef<any>;
Then we adjust the expander's HTML to render the received content.
<div class="header" (click)="toggleExpand()">
{{heading}}
<i *ngIf="!expanded" class="fas fa-chevron-down"></i>
<i *ngIf="expanded" class="fas fa-chevron-up"></i>
</div>
<div class="content" *ngIf="expanded">
<ng-container [ngTemplateOutlet]="content"></ng-container>
</div>
this also changes the API and the usage of the expander
component.
<expander heading="Expand to see the current time" [content]="content">
</expander>
<ng-template #content>
<clock></clock>
</ng-template>
We now wrap the clock
component inside a ng-template
. We then use a template reference to pass this content down to our expander component.
Let’s have a look if it changed the behavior of the lifecycles.

The life cycle hooks are now called once the component gets rendered or destroyed. Basically, every time our expanded
flag changes.
But, the API is less readable. A lot of devs prefer the readability of the ng-content
approach. Inlining the projected content into the tags of a component is more readable vs. passing it via template reference.

With the ng-content
approach you already know at first sight that the clock is projected to the expander. It’s less evident in the ng-template
approach.
Mimic ng-content like API with ng-template
Even though the approach shown above is the most common approach on how to use ng-template
we have other ways of using it. We are going to use a method that mimics the ng-content
API. How about this as an API?
<expander>
<ng-template>
<clock></clock>
</ng-template>
</expander>
To support this approach, we use @ContentChild
to access the projected content. So instead of getting the content via @Input
property we access it via @ContentChild
.
@Input() content: TemplatRef<any>;
// becomes
@ContentChild(TemplateRef) content: TemplateRef<any>;
We project the content with a convenient API and have the life cycle hooks called at the correct time.
Does this mean that we should always prefer template ref vs ng-content?
No. ng-content
is the easier approach with the cleaner API. It makes perfect sense for simple scenarios where you just project content without rendering it dynamically.
ng-template
on the other hand makes sense once you start to render the projected content dynamically. So basically once you find your ng-content
wrapped with an ngIf
you should consider using ng-template
.
ng-template
also makes sense for scenarios where you want to render the same content in multiple places.