Ng Deep Is Deprecated in Angular. Here is what to use instead.
Ng Deep Is Deprecated in Angular. Here is what to use instead.

Maybe you've read that ng-deep is deprecated inside Angular. But still lots of people are using it because this is the most comfortable way to style child components. This is why in this post I will show you alternatives to ng-deep when we need to style something.

Basic project

I already prepared for us a small project.

Initial project

Here we have a button with the icon and some text. This is the markup inside our app.component.html

<custom-button text="Foo" icon="▼"></custom-button>

So we have here a custom-button component with 2 inputs text and icon.

<button class="custom-button">
  <span *ngIf="icon" class="icon">{{ icon }}</span>
  {{ text }}
</button>

This is our custom-button and inside we render text and an icon if we have it.

export class ButtonComponent {
  @Input() icon?: string;
  @Input() text: string = 'Not defined';
}

Here is how our component look like.

What is the problem? Our custom button is styled a little bit.

.custom-button {
  padding: 5px;
  border: 1px solid grey;
}

.icon {
  margin-right: 5px;
}

Typically in a huge project where you are not working alone you might want to reuse this component in another place. But then for sure you will see that some styles are not exactly like you need them. Let's say that for your specific case you need to override border of the button.

Ng-deep

Most of the people will use for this ng-deep. We just to our parent component and override with it some scoped styles of child component.

:host {
  ::ng-deep {
    .custom-button {
      border: 1px solid red !important;
    }
  }
}

We put ng-deep inside the :host to isolate it's styles inside the parent.

NgDeep

As you can see in order to apply these styles we need to write important.

Ng-deep is the easiest variant and if we are talking about styling third-party libraries this is the only way.

Without ng-deep you can't style a third-party library because you don't have access to components inside.

Angular doesn't provide for us an alternative to ng-deep if it is removed. ng-deep won't be removed tomorrow but it will be removed at some point which means we must eliminate ng-deep from our projects. This is why the question is what alternatives do we have.

Configurable state

Another possibility for our case would be to provide some inputs to our child components to have different styling in different cases. In our button we already have icon and text so we can define something which will be reusable for other developers.

export class ButtonComponent {
  @Input() icon?: string;
  @Input() text: string = 'Not defined';
  @Input() isError = false;
}

Now we can add some styles to handle error properly.

<button class="custom-button" [ngClass]={error: isError}>
  <span *ngIf="icon" class="icon">{{ icon }}</span>
  {{ text }}
</button>

We applied error class if we got isError in the input. Now we just need to adjust our styles.

.error {
  border: 1px solid red;
}

Now we can provide an additional input if we want to apply error to our button.

<custom-button text="Foo" icon="▼" [isError]="true"></custom-button>

As you can see in browser it works the same but we didn't use ng-deep.

We add inputs for different states to style our component differently.

Host context

Another variant which is really similar to our input variant is by using host-context.

.custom-button {
  padding: 5px;
  border: 1px solid grey;

  :host-context(.theme-red) & {
    border: 1px solid red;
  }
}

This code means that if we apply a class theme-red to our custom-button we will get different styling.

<custom-button text="Foo" icon="▼" class="theme-red"></custom-button>

Now just by applying a class from the parent we can change how our css looks like. As you can see it is super similar to the input and I always prefer to use inputs instead.

Custom properties

Now we are coming to custom properties. This variant is the most flexible for me. We can specify what CSS rules you want to allow for overriding through the API that you create.

.custom-button {
  padding: 5px;
  border-color: var(--custom-button-border-color, grey);
  border-width: 1px;
  border-style: solid;
}

Here we used var notation. It means that we want to use --custom-button-border-color if it is provided from the outside. If it is not provided we will use grey.

:host {
  --custom-button-border-color: red;
}

Now in our parent it is enough to set this variant to a value that we need and it will be provided in the child component.

This way is the most flexible but requires access to the child component which won't be possible with third-party libraries for example.

And actually if you want to learn Angular with NgRx from empty folder to a fully functional production application make sure to check my Angular and NgRx - Building Real Project From Scratch course.

📚 Source code of what we've done