Unlocking the Power of the Directive Composition API in Angular

Leader posted 2 min read

-

In Angular, Directives are powerful building blocks that let you attach behavior to HTML elements. But as applications grow in complexity, managing shared logic across components becomes a real challenge. That's where the Directive Composition API comes into play—a modern Angular feature that promotes better code organization and reusability.

In this post, we’ll explore what the Directive Composition API is, the problems it solves, and how you can use it to write cleaner, more modular Angular code.


What Is the Directive Composition API?

Traditionally, Angular components expose behavior through inputs and outputs, or by using inheritance to share logic. However, these approaches can quickly become unwieldy, especially when different components require overlapping but not identical functionality.

The Directive Composition API lets you attach existing standalone directives to a component’s host element—from within the component’s TypeScript class. This means you can share behavior without exposing unnecessary APIs or using inheritance.

Here’s what it looks like in action:

@Directive({
  selector: '[myDirective]',
  standalone: true
})
export class MyDirective {
  // behavior logic
}

@Component({
  selector: 'my-component',
  standalone: true,
  hostDirectives: [MyDirective], //  Composing directive into host
  template: `<div>Reusable behavior is here</div>`
})
export class MyComponent {}

What Problem Does This Solve?

Let’s imagine a common scenario: you have three components—a button, a toggle, and a spinner.

  • All three need a color-changing capability.
  • The button and toggle also need a disabled state.
  • The spinner does not need the disabled functionality.

Option 1: Inputs on Each Component

You could add @Input() properties like color and disabled to each component, duplicating logic across them. This works but leads to repetition and tight coupling.

Option 2: Component Inheritance

You might consider a base class that all components extend. But this causes problems too:

  • The spinner ends up with disabled support it doesn’t use.
  • You’re exposing an API that’s irrelevant in some contexts.
  • Inheritance is rigid and doesn’t scale well.

The Better Way: Use Directive Composition

Instead of inheritance or bloated APIs, Angular now allows you to compose directives directly into components.

You can define functionality as standalone directives:

@Directive({
  selector: '[appDisable]',
  standalone: true
})
export class DisableDirective {
  @Input() disabled = false;
  // logic to handle disabled state
}

@Directive({
  selector: '[appColor]',
  standalone: true
})
export class ColorDirective {
  @Input() color = 'primary';
  // logic to apply color
}

Then, in each component, you compose only the directives you need:

@Component({
  selector: 'app-button',
  standalone: true,
  hostDirectives: [DisableDirective, ColorDirective],
  template: `<button>Click Me</button>`
})
export class ButtonComponent {}

@Component({
  selector: 'app-spinner',
  standalone: true,
  hostDirectives: [ColorDirective], // No disable directive here!
  template: `<div class="spinner"></div>`
})
export class SpinnerComponent {}

Benefits:

  • Granular functionality: Only use what you need.
  • Better reusability: Logic lives in directives, not components.
  • Cleaner APIs: No unused inputs or inherited properties.
  • Improved maintainability: Easier to test and debug individual behaviors.

Important Notes

  • All directives used in hostDirectives must be standalone:

    @Directive({
      standalone: true,
      selector: '[yourDirective]'
    })
    
  • hostDirectives is only supported in standalone components, part of Angular’s modern architecture introduced in v14+.

Conclusion

The Directive Composition API is a powerful new tool in Angular’s arsenal, helping you compose behavior into components without inheritance or bloated inputs. By leveraging standalone directives, your components become smaller, cleaner, and more focused.

Whether you're building design systems, interactive forms, or real-time UIs, directive composition gives you the flexibility and modularity you need.


If you read this far, tweet to the author to show them you care. Tweet a Thanks

More Posts

Angular Gets Static: Exploring AnalogJS and the Power of SSG

Sunny - Sep 25

The Power of Higher Order Functions in JavaScript: Writing Cleaner and More Efficient Code

Mubaraq Yusuf - Mar 19

Unlocking the Power of Generators in JavaScript

Mubaraq Yusuf - Jan 4

Exploring the Magic of Python’s dataclass Module

alvisonhunter - Oct 3

Unlocking the Potential of Web Sockets in Modern Web Development

Aziz Ibrahim - May 22
chevron_left