If you’re a front-end developer or designer experimenting with Angular, you might have run into some of the following issues while building a UI:

  • CSS classes getting out of control and HTML turning messy
  • Struggling to clean up or restructure bloated CSS
  • Feeling overwhelmed by naming conventions like BEM

If any of this sounds familiar, I have a few tips to help you create a more consistent and maintainable UI using Angular 2—and more specifically, transclusion.

Don’t be intimidated by the term transclusion. It’s simpler than it sounds. To paraphrase Scotch.io:

Transclusion lets you define a fixed template for a UI element while allowing dynamic content to be injected into predefined slots.

In short, you can build reusable UI components—like a card or a panel—and easily slot in different content. Let’s break this down.

How transclusion works and why components are the key

Angular 2 introduces the concept of components—self-contained elements that use custom HTML selectors. They let you scope your CSS without worrying about leaking styles or creating tangled, hard-to-maintain code.

This isolation is the foundation for building clean, modular UIs.

To use transclusion in Angular 2, you use the <ng-content> tag inside your component’s HTML. This defines where dynamic content can be placed.

You can even use the select attribute to target specific slots—more on that in a second.

Building a panel pomponent

Panel component

Let’s walk through a simple example: a reusable Panel component with a header and content area.

Step 1: Create the component (panel.ts)

//
// panel.ts
//
import {Component} from 'angular2/core';

@Component({
selector: 'panel',
template: `
      <div class="panel">
        <div class="panel-header">
          <ng-content select="panel-header"></ng-content>
        </div>
        <ng-content></ng-content> 
      </div>
  `
})
export class PanelComponent {}

We use the select attribute to define where specific content will be injected.

<ng-content select="panel-header"></ng-content>`

Note: To make custom tags like <panel-header> work, you’ll need to include this in your app module:

schemas: [ CUSTOM_ELEMENTS_SCHEMA ]

Otherwise, Angular will throw a template error.

Step 2: Add component styles (panel.sass)

//
// panel.sass
//
.panel
  background-color: #fff
  border: 1px solid transparent
  border-radius: 4px
  -webkit-box-shadow: 0 1px 1px rgba(0,0,0,.05)
  box-shadow: 0 1px 1px rgba(0,0,0,.05)
  color: #333
  
  .panel-header
    padding: 10px 15px
    border-bottom: 1px solid transparent
    border-top-left-radius: 3px
    border-top-right-radius: 3px
    background-color: #f5f5f5
    border-color: #ddd

Style your panel component and import the stylesheet into your main SASS/SCSS file.

Step 3: Use your panel anywhere

<panel>
    <panel-header>
        Panel title
    </panel-header>
        Panel content 
</panel>

Once set up, you can use your panel component anywhere in your application with clean, readable HTML.

Why this matters

Our Panel component has a clear, consistent structure. You can now reuse it across your project without writing CSS classes directly in your templates.

This modular approach allows you to build complex UIs from small, reusable building blocks—like LEGO. While it requires a bit of upfront planning, especially for beginners, the long-term benefits are worth it: cleaner code, better scalability, and easier maintenance.

Understanding the select Attribute in <ng-content>

In our example, we used select with an HTML tag, but Angular’s transclusion mechanism offers more flexibility. The select attribute allows you to match content not just by tag name, but also by class, attribute, or attribute value. Here’s how it works:

1. Matching by HTML Tag

<ng-content select="panel-header"></ng-content>

DOM Output:

<panel-header></panel-header>

2. Matching by CSS Class

<ng-content select=".panel-header"></ng-content>

DOM Output:

<div class="panel-header"></div>

3. Matching by Attribute

<ng-content select="[panel-header]"></ng-content>

DOM Output:

<div panel-header></div>

4. Matching by Attribute Value

<ng-content select="[panel-header='true']"></ng-content>

DOM Output:

<div panel-header="true"></div>