Angular 10 Transclusion: When and Why You`ll Need It

If you are into Angular there was a time you had a component that could hold multiple other components.

In my latest use case this was actually a basic styled component, meet the card component.

It has a border, box-shadow, title, and icon if needed.

Inside this card component, multiple items could be rendered, a blog post, a podcast, a content page, etc.

If you are thinking but huh I can add this card styling to each component? Yes, but imagine the card layout changes, you now have to edit component using that styling. Creating a top-level component and transcluding the components inside you only have to update the piece of code once.

So this is where Angular transclusion comes in, it means we have a swappable piece in our component.

Angular Transclusion

A super high-level visual representation.

High-level transclusion

Creating our transclusion component

In this article, we will use our master Angular 10 Tailwind application from GitHub.

Now let's start by generating this card component.

ng generate component Card

This will generate our basic component, let's open up the TS file and make the following changes.

import {Component, Input} from '@angular/core';

@Component({
  selector: 'app-card',
  templateUrl: './card.component.html'
})
export class CardComponent {
  @Input() title: string;
  @Input() icon: string;

  constructor() {}
}

Here you can see we have two Angular @Input attributes, which will be able to add a title and optional icon to our card.

Now we can add our HTML.

<div class="flex flex-row mb-5 overflow-hidden bg-white rounded-lg shadow-lg">
  <div class="flex flex-col w-full p-6 text-dark-gray-600">
    <div class="flex justify-between mb-4">
      <span class="uppercase">{{ title }}</span>
      <i *ngIf="icon">{{ icon }}</i>
    </div>
    <ng-content></ng-content>
  </div>
</div>

This is where the magic comes in, you see that <ng-content> element, that will render anything inside our card component.

Let's try it out.

Open up the welcome.component.html and add the following code:

<div class="container p-6 m-auto">
  <app-card title="Welcome" icon="✌️">
    <h1>Anything in here is content!</h1>
  </app-card>
</div>

So, here we add a new app-card with a static title and icon for this example.

Note: You could make this dynamic by using [title]="dynamicProp"

And inside the app-card, we specify what is rendered inside the ng-content. In our case, a simple header for now.

The result so far:

Angular card transclusion

Rendering a component inside another Angular component

This use-case was fun, but we want to render some other component in it most of the time.

Let's generate a podcast component.

ng generate component Podcast

For now, let's add some static HTML for this demo purpose.

<div
  class="w-full h-24 mb-3 bg-center bg-cover rounded-lg"
  [style.background-image]="'url(' + img + ')'"
></div>
<strong class="block mb-2">Episode 142: Ionic vs Flutter</strong>
<p>
  In todays episode we are talking to Michael about how Flutter compares to Ionic.
</p>
<audio controls class="w-full mt-4">
  <source src="podcast.ogg" type="audio/ogg" />
  <source src="podcast.mp3" type="audio/mpeg" />
  Your browser does not support the audio tag.
</audio>

Just a simple setup. For now, the image is loaded from the Typescript:

img: string =
  'https://images.unsplash.com/photo-1606592641978-bbfa15596820?ixlib=rb-1.2.1&ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&auto=format&fit=crop&w=1950&q=80';

Now let's add this component to our card:

<app-card title="Podcast" icon="🎧">
  <app-podcast></app-podcast>
</app-card>

This will now render as:

Angular transcluding another component

Naming the transclusion section

One cool thing we can do is name the transclusion slot. Open your card-component.html file and change the ng-content to include this:

<ng-content select="[body]"></ng-content>

Now we can change our welcome.component.html to look as such:

<app-card title="Podcast" icon="🎧">
  <app-podcast body></app-podcast>
</app-card>

Angular multiple-slot transclusion

The power of naming these slots comes in the ability to add more than one to a component.

Let's say we want a body and a footer part that could be dynamic.

We already have this body part now, but let's make it possible to add a footer.

Change the card.component.html:

<div class="flex flex-row mb-5 overflow-hidden bg-white rounded-lg shadow-lg">
  <div class="flex flex-col w-full p-6 text-dark-gray-600">
    <div class="flex justify-between mb-4">
      <span class="uppercase">{{ title }}</span>
      <i *ngIf="icon">{{ icon }}</i>
    </div>
    <ng-content select="[body]"></ng-content>
    <ng-content select="[footer]"></ng-content>
  </div>
</div>

As you can see, we added a second ng-content section called footer.

Now let's also modify our welcome.component.html to add some data to it.

<app-card title="Podcast" icon="🎧">
  <app-podcast body></app-podcast>
  <div class="mt-2 italic" footer>Thank you for listening</div>
</app-card>

So this example is a bit weird, but you can see we have two transclusion slots.

This will result in the following result:

Angular multiple transclusion slots

Rendering different components

As mentioned, the real power is to render multiple component types into one card component.

Let's define another component for a podcast.

ng generate component Video

Let's make some changes to the video.component.html file.

<strong class="block mb-2">6 everyday JavaScript tips you must know</strong>
<p>In this video i'll guide you to create a super cool Todo list</p>
<iframe
  width="100%"
  height="250px"
  src="https://www.youtube.com/embed/svFffyg_m0M"
  frameborder="0"
  allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
  allowfullscreen
></iframe>

Now we can add this block to our welcome.component.html.

<app-card title="Video" icon="📼">
  <app-video body></app-video>
  <div class="mt-2 italic" footer>Thanks for viewing</div>
</app-card>

The result

Angular rendering multiple components with transclusion

In the image above, you can see that the card styling is the same. We have the title, icon, and a new podcast component in the middle.

I hope you've seen the massive good use-cases of using transclusion to make our lives easier.

You can find the finished result on the following GitHub repo.

Thank you for reading, and let's connect!

Thank you for reading my blog. Feel free to subscribe to my email newsletter and connect on Facebook or Twitter

Comments (2)

Rafael dos Santos Miguel Filho's photo

Amazing, thanks for sharing! 🚀👏

Chris Bongers's photo

You're welcome Rafael, thanks for the comment 🤟