Skip to content

Angular content projection using ng-content

ContentProjectionNgContentCover

Content projection allows us to inject content anywhere else in our template. Content projection is useful when building reusable components. For example, instead of duplicating the same component in order to change only a part of the data, we might decide to use content projection.

We can project/inject content using the <ng-content></ng-content> tag in the HTML of our components. The content we put inside our component tag is the content that is projected into the component.

Additionally, we can create multiple projection spaces using multi-slot projection by designating certain sections to an alias, by specifying a select value.

Single-slot content projection

We mentioned above, that we can simply project content using a single data point. We can project as much content as we want, but we have only one insertion point.

How all of this works we can inspect best by creating a simple component that we will experiment on. Below you can find the template of the User Card component. (The full application can be found on the end inside the Stackblitz IDE). user-card.component.html:

<div class="wrapper">
  <div class="basic-info">
    <div class="image">
      <img
        src="https://avatars.dicebear.com/api/male/tom.svg?background=%230000ff"
      />
    </div>
    <div class="personal-data">
      <h3>Name:</h3>
      <p>Tom</p>
      <h3>Surname:</h3>
      <p>Doe</p>
    </div>
  </div>
  <div class="more-data">
    <h3>Likes:</h3>
    <p>Football, swimming, beer</p>
  </div>
</div>

We created a simple Card component that just displays some information about an imaginary person. For the colorful avatar, we used the super cool Dicebear API. We can then import this template into our app.component.html by writing:

<app-user-card></app-user-card>

And we will get the following result:

Content Projection In Angular – Basic Card Component

Let’s say we want to display some more data about our person. We could simply add more data into the template, we could also use the @Input() decorator to add more properties. But in this case, we will use content projection.

The simplest way to project data is to use <ng-content></ng-content> tags inside the template and then inject data into the component selector. This will be our updated user-card.component.html. Notice the new element at the end there – the <ng-content> tags.

<div class="wrapper">
  <div class="basic-info">
    <div class="image">
      <img
        src="https://avatars.dicebear.com/api/male/tom.svg?background=%230000ff"
      />
    </div>
    <div class="personal-data">
      <h3>Name:</h3>
      <p>Tom</p>
      <h3>Surname:</h3>
      <p>Doe</p>
    </div>
  </div>
  <div class="more-data">
    <h3>Likes:</h3>
    <p>Football, swimming, beer</p>
  </div>
  <ng-content></ng-content>
</div>

Then in our app.component.html we can project our data by writing this:

<app-user-card>
  <div class="more-data">
    <h3>Favorite day:</h3>
    <p>Sunday</p>
  </div>
</app-user-card>

Great! Now we have our projected content. But… Something is wrong! Even though we managed to inject our data, it seems like our styling is all messed up!

Content Projection in Angular – Projected Content

How to fix this? The content in <ng-content> is insulated from the component, it can’t see the component’s attribute or styling. To fix this, we will use the ::ng-deep modifier, which will go beyond the component to apply stylings. We can imagine the CSS ‘leaking’ through to other components. Our user-card.component.css will then be:

::ng-deep p {
  margin-top: 0;
  margin-bottom: 5px;
  color: #ecd9b5;
}

::ng-deep h3 {
  margin-top: 0;
  margin-bottom: 5px;
  color: #ecd9b5;
}

Multi-slot data projection

Now, let us designate multiple sections for data projection. We do that by specifying the select property on ng-content, and not only we can inject multiple data, but we can also tell the data where to appear! Notice the two ng-content tags with different select values, in the user-card.component.html template below, one put above our ‘Likes’ attribute, and the other below:

<div class="wrapper">
  <div class="basic-info">
    <div class="image">
      <img
        src="https://avatars.dicebear.com/api/male/tom.svg?background=%230000ff"
      />
    </div>
    <div class="personal-data">
      <h3>Name:</h3>
      <p>Tom</p>
      <h3>Surname:</h3>
      <p>Doe</p>
    </div>
  </div>
  <ng-content select="[fav-day]"></ng-content>
  <div class="more-data">
    <h3>Likes:</h3>
    <p>Football, swimming, beer</p>
  </div>
  <ng-content select="[dislikes]"></ng-content>
</div>

In order to inject data into both projection slots we just need to assign selectors to our projected elements. Selectors can be tag names, CSS classes and the :not pseudo-classes. Our app.component.html will look like this now:

<app-user-card>
  <div class="more-data" fav-day>
    <h3>Favorite day:</h3>
    <p>Sunday</p>
  </div>
  <div class="more-data" dislikes>
    <h3>Dislikes:</h3>
    <p>The alarm clock</p>
  </div>
</app-user-card>

For the complete project, please look at the Stackblitz below:

Final Words

Hopefully, you liked the first of many Angular articles that we will have on this blog. Content projection has some great use cases. We could use it to create a wrapper component around some of our data, templates that can host any kind of different data, or simply just to inject different data in specific slots. For more information about content projection please check the official Angular documentation.

For more articles please click below, or check the blog.

1 thought on “Angular content projection using ng-content”

  1. Pingback: Angular ContentChild -

Comments are closed.