ng-deep is a special selector in Angular that you should use very carefully. I often come across ng-deep in code reviews, where I investigate further to see if it is really necessary. In this post, I will talk a little bit about ng-deep: what it does, where it is useful and also what alternative approaches we can use instead of ng-deep.

What does ng-deep do?

One of the great features of Angular is that components support view encapsulation.

With view encapsulation, Angular creates a scope for every component at runtime so that the styles of a component don’t affect the rest of the application.

While this protects logic from leaking to other components, it also makes it more difficult to style elements in child components.

To overcome this issue, Angular introduced a special selector ng-deep.

ng-deep selector completely disables the view encapsulation policy of a child component, leaking styles into components you don’t want to change.

ng-deep example

In the example below, the parent component is styling a button within the child component using ng-deep selector.

import { Component } from "@angular/core";

@Component({
  selector: "app-parent",
  template: `<app-child></app-child>`,
  styles: [
    `
      ::ng-deep .hello {
        color:red;
      }
    `
  ]
})
export class ParentComponent { }

@Component({
  selector: "app-child",
  template: ` <button class="hello" (click)="sayHello()"> Hello </button> `,
  styles: [``]
})
export class ChildComponent {}
Code language: JavaScript (javascript)

So that’s what ng-deep looks like – it makes it easy to workaround the view encapsulation.

Why you need to avoid ng-deep

The main reason for avoiding ng-deep is because the selector has been deprecated for years.

Angular also plans to drop support for ng-deep in a future release so your next Angular upgrade may be more difficult.

Also, ng-deep can lead to unexpected outcomes.

Since ng-deep pierces the view encapsulation policy, you might find your deep styles affect a component unintentionally.

The only case you cannot avoid ng-deep

Based purely on my experience, there is only one situation where you cannot avoid ng-deep.

And that’s when you’re dealing with components from third party libraries like Bootstrap, Material etc.

This is because you simply cannot change the third party component code, so you have to use ng-deep.

Let me clarify an important point before we start, there is no true replacement to ng-deep.

Alternative #1 – :host selector

In situations where you want to apply styles directly to a child component (not its elements), :host selector can be used instead of ng-deep.

:host selector targets the child component as a whole, instead of elements inside its template.

Any style marked with :host in a child component can be overridden by a parent component without using ng-deep.

@Component({
  selector: "app-parent",
  template: `<app-child></app-child>`,
  styles: [
    `
      app-child {
        background-color:green;
      }
    `
  ]
})
export class ParentComponent { }

@Component({
  selector: "app-child",
  template: `
  <button class="hello"> Hello </button>
  `,
  styles: [
    `
    :host {
      background-color:red;
      width:200px;
      height:200px;
      display: block;
    }
    `
  ]
})
export class ChildComponent {}
Code language: TypeScript (typescript)

This approach works well for simple scenarios where you want to modify the styles of the child component and not its elements.

Alternative #2- CSS variables

In more complex cases, where you need to apply styles to elements of Another alternative to ng-deep is to use CSS variables to share values between parent and child component.

Angular view-encapsulation does not cover CSS variables so it is possible for the parent to define a variable like --text-colour and the child component can make use of it in its stylesheet file.

This is especially useful for complex scenarios where you want the child component to be reusable.

import { Component } from "@angular/core";

@Component({
  selector: "app-parent",
  template: `<app-child></app-child>`,
  styles: [
    `
    app-child { /* declare variable for child */
      --text-colour: green;
    }
    `
  ]
})
export class ParentComponent { }

@Component({
  selector: "app-child",
  template: `
  <button class="hello"> Hello </button>
  `,
  styles: [
    `
    button { /* use variable from parent, or default value of 'red'*/
      color: var(--text-colour, red); 
    }
    `
  ]
})
export class ChildComponent {}

Code language: TypeScript (typescript)

It is worth noting that major browsers have full support for CSS variables. This, of course, does not include IE.

It is also possible to bind CSS variables in the HTML template of the parent component:

import { Component } from "@angular/core";

@Component({
  selector: "app-parent",
  template: `<app-child [style.--text-colour]="'green'"></app-child>`,
  styles: [` `]
})
export class ParentComponent { }

Code language: TypeScript (typescript)

The only limitation of this approach is that you need to modify the child component. This may be a problem if you’re trying to style a third party component.

Umut Esen

Software Engineer specialising in full-stack web application development.

Leave a Reply