Angular Content Projection: How to Guide

August 26, 2024  by Jan Kruse j1n ‐ 5 min. read

Read how to use content projection in Angular effectively in this article. From the basics to advanced techniques - practical examples to make your components flexible and maintainable.

PC screen on a wooden desk with IDE open

Have you often wondered which structure makes the most sense for Angular components in your project? Or why the data flow becomes increasingly confusing over time? Perhaps you’re wondering whether an @Input() is really necessary for a certain component - or what alternatives there are?

Then you should take a closer look at a useful tool in the Angular toolbox: the content projection with <ng-content/>.

What is content projection?

In the context of Angular, content projection describes a technique for inserting content from a parent component into a child component 1.

Compared to the @Input decorator, Content Projection offers the advantage that a greater variety of elements can be transferred to the child component.

Possible contents:

  • Inner HTML
  • HTML elements
  • Styled HTML elements
  • Other Angular components

Example

To make sure we understand everything, let’s look at a practical example.
In the following app.component.ts file, we have a parent component that uses the HeadlineComponent (child component).

@Component({
  selector: 'app-root',
  standalone: true,
  imports: [HeadlineComponent],
  template: '<app-headline>Example Headline</app-headline>',
  styleUrl: './app.component.scss'
})
export class AppComponent {}

The content Example Headline is projected into the <app-headline> component using Content Projection.

@Component({
  selector: 'app-headline',
  standalone: true,
  imports: [],
  template: '<h1><ng-content></ng-content></h1>',
  styleUrl: './headline.component.scss'
})
export class HeadlineComponent {}

The result in the DOM is an <h1> tag, with Example Headline as content.

<h1>Content Projection</h1>

If you want to look at the code yourself, you can find it in this repository, on the examples/basic branch.

Multi-slot content projection

Sometimes it is necessary to create more complex layouts that render different parts of content in different places. Angular offers the possibility to project multiple contents into one component by using multiple <ng-content> placeholders. The examples below can also be found at GitHub.

Component with multiple <ng-content> elements:

<div class="card">
  <h1>
    <ng-content select="[card-title]"></ng-content>
  </h1>
  <ng-content select="card-body"></ng-content>
  <ng-content select=".card-footer"></ng-content>
</div>

We recognize the familiar <ng-content>.
Here, the select attribute is used to project specific content into the corresponding placeholders. Example of use:

<app-custom-card>
  <card-title>Hello</card-title>
  <card-body>Welcome to the example</card-body>
</app-custom-card>

Assignment of the projection

There are several ways to determine which <ng-content> element is used.

Selector

Firstly, as in the example above, we can use <card-title> and <card-body> to reference the select attributes of the respective <ng-content>.
It is important to note that the parent component defines CUSTOM_ELEMENTS_SCHEMA as schemas. Otherwise Angular would search for an element with the selector <card-title>. We therefore tell Angular that these are valid elements.

@Component({
  ...
  schemas: [CUSTOM_ELEMENTS_SCHEMA]
})

Div-Attribut

<ng-content select="[card-title]"></ng-content>
<div card-title>
  Card Title
</div>

Classes/Id-Selector

<ng-content select=".card-title"></ng-content>
<div class="card-title">
  Card Title
</div>

Summary

Up to this point, we have covered the basics of content projection in Angular and used examples to show how content can be projected from a parent component into a child component. We have explained the simple use of <ng-content> and seen how to define multiple placeholders in a component to specifically insert different content.

In the following, we will focus on when it makes sense to use this technique.

When does ng-content make sense?

  • For more complex layouts. By using <ng-content>, the created component can accommodate a wide variety of content (see card example).
  • Multiple slots with selective content: With the option to use multiple ng-content areas using the select attribute, you can control which content is displayed in which area. This is particularly useful for more complex layouts in which different parts of the component should contain different content.
  • If you want to keep the data in the parent component and the content should only be displayed by the children. This saves you @Input() and @Output(). This makes your code clearer, keeps data streams short and is easier to maintain for you and your team.
  • Easier division into smart and presentation components.
  • If a component should display additional content from a parent component, but you don’t want to change the original logic.
  • If you want to project HTML directly.

When should I go without ng-content?

  • Static content, if the content does not change, there is no advantage to using <ng-content>.
  • If you want to type or transform input data, this is easier to achieve with classic inputs.
  • Performance considerations with too much dynamic content: If a component displays frequently changing or very dynamic content, there may be performance issues as Angular has to evaluate the entire DOM.

What else should I know about ng-content?

In addition to the basic use cases, there are some additional aspects of <ng-content> that you should know.

  1. <ng-content> should not be conditionally integrated with @If etc., as Angular also creates the DOM elements for the non-visible placeholders 1.

  2. It is possible to set a default value for <ng-content>. This is placed between the opening and closing ng-content-tag, as in the example below.
    The default is displayed if there is no content that corresponds to the specified select of an <ng-content>.

<div class="card">
   <h1>
    <ng-content select="[card-title]">DEFAULT-Value</ng-content>
   </h1>
   <ng-content select="card-body"></ng-content>
   <ng-content select=".card-footer"></ng-content>
</div>
  1. Furthermore it is also possible to use a fallback <ng-content>. If we look at the card example again, we can see that there are several ng-content blocks with a select. If the parent component tries to project content that does not correspond to one of the select, it will not be displayed.
    This problem can be solved by simply adding <ng-content/>. The projected content that cannot be assigned to a select is projected into this fallback <ng-content></ng-content>.

  2. With the help of ngProjectAs we can specify a CSS selector. Whenever an element with ngProjectAs is checked against a <ng-content> placeholder, Angular compares with the ngProjectAs value instead of the identity of the element. Here is another example:

<app-custom-card>
  <div card-title>
    Title
  </div>
  <h3 ngProjectAs="card-body">Hello</h3>
  <div class="card-footer">Footer</div>
</app-custom-card>

In this case, the card-body will be rendered as an h3 HTML element 1.

Sources


  1. Angular: Content projection with ng-content, in: Angular , o.D., https://angular.dev/guide/components/content-projection (visited on 08.26.2024). ↩︎ ↩︎ ↩︎