Ch. 4 · Angular

Angular Host Directives and Composition

Attach behavior to elements with attribute directives, bind to the host with the host property, and compose directives with hostDirectives.

~2 min readintermediateupdated Oct 5, 2026

An attribute directive adds behavior to an existing element without a template of its own. It can bind to the host element through the host property and compose other directives with hostDirectives, which lets a component adopt a directive’s behavior as part of its public API.

Before you start

You should be comfortable with directives, element refs and change detection. This article covers host binding and composition; it assumes basic directive syntax.

Step-by-step walkthrough

Step 1: Bind to the host declaratively

Use the host object on the directive to set classes, attributes and listeners instead of manipulating the DOM directly. host: { '[class.active]': 'isActive', '(click)': 'toggle()' } keeps the bindings visible in the metadata and works with server rendering.

Step 2: Compose instead of duplicating

hostDirectives: [Tooltip] makes a directive apply another directive to its host, so a component can expose tooltip behavior without repeating it. You can also re-export the host directive’s inputs and outputs so consumers can set them on the component.

Step 3: Prefer host bindings over Renderer calls

Reach for ElementRef and Renderer2 only when there is no declarative binding, because direct DOM access assumes a browser and breaks server rendering. Host bindings and attribute bindings are the idiomatic path and keep the directive testable.

Worked scenario

The directive sets a class and listens to clicks through the host metadata.

import { Directive, HostListener } from '@angular/core';

@Directive({
  selector: '[appToggle]',
  host: { '[class.on]': 'on' },
})
export class ToggleDirective {
  on = false;

  @HostListener('click')
  toggle(): void {
    this.on = !this.on;
  }
}
TypeScript

Walk through the example

The host binding toggles the on class from the on field, and @HostListener handles the click. The directive never touches the DOM directly, so it renders correctly on the server and the bindings are declared in one place. Any element with appToggle gains the behavior.

Common mistake

Using ElementRef.nativeElement to add classes or listeners, which bypasses Angular’s change detection and fails under server rendering. Another is duplicating a directive’s logic in a component instead of composing it with hostDirectives.

Verify the behavior

Apply the directive and assert the class toggles on click. Test the directive in isolation with a host component. Add a hostDirectives composition and confirm the composed behavior works and its re-exported inputs are settable from the template.

Interview exercise

When would you use hostDirectives instead of adding the directive in the template?

Answer and reasoning

When a component should always carry a directive’s behavior as part of its own API, so consumers get it automatically and can set its inputs without remembering to add the directive. It also keeps the selector list shorter and the behavior encapsulated. If the behavior is optional or configured per use, adding the directive in the template is clearer.

Continue learning

Compare directive composition in Angular content projection and standalone imports. Read the Angular attribute directives guide and try the Angular interview questions.

More in Angular

read ✓Angular · mid

Angular Defer Blocks and Lazy Rendering

Load template regions on demand with @defer, choose a trigger, and use placeholders without delaying critical content.

~2 min readread →
read ✓Angular · hard

Angular Global Error Handling

Catch uncaught errors with a custom ErrorHandler, distinguish client from HTTP errors, and surface safe messages to users.

~2 min readread →
read ✓Angular · mid

Angular Pure and Impure Pipes

Understand why pure pipes run only when inputs change, what makes a pipe impure, and how to avoid the performance cost.

~3 min readread →
esc