Skip to main content

Angular View Encapsulation

Introduction​

When building Angular applications, you'll create multiple components, each with its own templates and styles. A key question arises: how do styles from one component affect other components? This is where View Encapsulation comes into play.

View Encapsulation determines how the styles defined in a component affect the rest of the application. It's Angular's way of implementing style isolation, allowing you to write CSS that applies only to a specific component without affecting others.

In this tutorial, we'll explore Angular's View Encapsulation modes, how they work, and when to use each one.

What is View Encapsulation?​

View Encapsulation is Angular's mechanism for controlling how component styles are applied to the DOM. It addresses a fundamental challenge in web development: CSS's global nature can lead to style conflicts between components.

Angular offers three encapsulation strategies:

  1. Emulated (default): Adds unique attributes to elements and scopes styles to the component
  2. None: Applies styles globally without any scoping
  3. ShadowDom: Uses the browser's native Shadow DOM API for true style isolation

View Encapsulation Modes​

1. Emulated Encapsulation (Default)​

Emulated encapsulation is Angular's default mode. It simulates Shadow DOM behavior by adding unique attributes to elements and transforming your CSS selectors to be component-specific.

How to use it:​

typescript
import { Component, ViewEncapsulation } from '@angular/core';

@Component({
selector: 'app-emulated-example',
template: `
<h2>Emulated Encapsulation (Default)</h2>
<p class="example-text">This paragraph has scoped styles</p>
`,
styles: [`
.example-text {
color: blue;
font-weight: bold;
}
`],
encapsulation: ViewEncapsulation.Emulated // This is the default, so it's optional
})
export class EmulatedExampleComponent { }

What happens in the DOM:​

Angular transforms your component's styles by adding unique attributes to elements. For example, it might render as:

html
<app-emulated-example _nghost-xuf-c1>
<h2 _ngcontent-xuf-c1>Emulated Encapsulation (Default)</h2>
<p _ngcontent-xuf-c1 class="example-text">This paragraph has scoped styles</p>
</app-emulated-example>

And your CSS becomes:

css
.example-text[_ngcontent-xuf-c1] {
color: blue;
font-weight: bold;
}

The _ngcontent-xuf-c1 attribute ensures these styles only apply to elements within this component.

2. None Encapsulation​

When you use ViewEncapsulation.None, Angular applies styles globally without any encapsulation. This means styles defined in the component will affect the entire application.

How to use it:​

typescript
import { Component, ViewEncapsulation } from '@angular/core';

@Component({
selector: 'app-none-example',
template: `
<h2>None Encapsulation</h2>
<p class="global-text">This paragraph has global styles</p>
`,
styles: [`
.global-text {
color: red;
font-style: italic;
}
`],
encapsulation: ViewEncapsulation.None
})
export class NoneExampleComponent { }

What happens in the DOM:​

Angular adds your styles to the <head> section of the document without any modifications:

html
<style>
.global-text {
color: red;
font-style: italic;
}
</style>

These styles will affect all elements with the class global-text throughout your application.

3. ShadowDom Encapsulation​

Shadow DOM encapsulation uses the browser's native Shadow DOM API to isolate the component's DOM and styles completely.

How to use it:​

typescript
import { Component, ViewEncapsulation } from '@angular/core';

@Component({
selector: 'app-shadow-example',
template: `
<h2>ShadowDom Encapsulation</h2>
<p class="shadow-text">This paragraph has truly isolated styles</p>
`,
styles: [`
.shadow-text {
color: green;
text-decoration: underline;
}
`],
encapsulation: ViewEncapsulation.ShadowDom
})
export class ShadowDomExampleComponent { }

What happens in the DOM:​

Angular creates a Shadow Root for your component and attaches your component's DOM to it:

html
<app-shadow-example>
#shadow-root (open)
<h2>ShadowDom Encapsulation</h2>
<p class="shadow-text">This paragraph has truly isolated styles</p>
<style>
.shadow-text {
color: green;
text-decoration: underline;
}
</style>
</app-shadow-example>

The styles are contained within the Shadow DOM boundary and cannot affect or be affected by styles outside the component.

Practical Examples​

Example 1: Component Library with Mixed Encapsulation​

Imagine you're building a UI component library. For shared components like buttons or form controls, you might want consistent styling across the application:

typescript
import { Component, ViewEncapsulation } from '@angular/core';

@Component({
selector: 'app-primary-button',
template: `
<button class="btn btn-primary">
<ng-content></ng-content>
</button>
`,
styles: [`
.btn {
padding: 8px 16px;
border-radius: 4px;
border: none;
cursor: pointer;
}
.btn-primary {
background-color: #0275d8;
color: white;
}
.btn-primary:hover {
background-color: #025aa5;
}
`],
encapsulation: ViewEncapsulation.None // Global styles for consistent buttons
})
export class PrimaryButtonComponent { }

Example 2: Dashboard Widget with Isolated Styles​

For a dashboard with multiple independent widgets, you might want to ensure styles don't leak between widgets:

typescript
import { Component, ViewEncapsulation } from '@angular/core';

@Component({
selector: 'app-weather-widget',
template: `
<div class="widget">
<h3 class="widget-title">Current Weather</h3>
<div class="widget-body">
<div class="temperature">72°F</div>
<div class="condition">Sunny</div>
</div>
</div>
`,
styles: [`
.widget {
border: 1px solid #ddd;
border-radius: 4px;
padding: 15px;
background: linear-gradient(to bottom, #e6f7ff, #ffffff);
}
.widget-title {
margin-top: 0;
color: #0275d8;
}
.temperature {
font-size: 2em;
font-weight: bold;
}
.condition {
font-style: italic;
}
`],
encapsulation: ViewEncapsulation.Emulated // Prevent styles from affecting other widgets
})
export class WeatherWidgetComponent { }

Example 3: Theme Toggle with ShadowDom​

For components that need complete style isolation, like a theme toggle that shouldn't be affected by global styles:

typescript
import { Component, ViewEncapsulation } from '@angular/core';

@Component({
selector: 'app-theme-toggle',
template: `
<div class="toggle-container">
<span class="label">Dark Mode</span>
<label class="switch">
<input type="checkbox" (change)="toggleTheme($event)">
<span class="slider round"></span>
</label>
</div>
`,
styles: [`
.toggle-container {
display: flex;
align-items: center;
gap: 10px;
}
.label {
font-size: 14px;
}
/* Custom toggle switch styling */
.switch {
position: relative;
display: inline-block;
width: 60px;
height: 34px;
}
.switch input {
opacity: 0;
width: 0;
height: 0;
}
.slider {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: #ccc;
transition: .4s;
}
.slider:before {
position: absolute;
content: "";
height: 26px;
width: 26px;
left: 4px;
bottom: 4px;
background-color: white;
transition: .4s;
}
input:checked + .slider {
background-color: #0275d8;
}
input:checked + .slider:before {
transform: translateX(26px);
}
.slider.round {
border-radius: 34px;
}
.slider.round:before {
border-radius: 50%;
}
`],
encapsulation: ViewEncapsulation.ShadowDom // Complete isolation
})
export class ThemeToggleComponent {
toggleTheme(event: any) {
document.body.classList.toggle('dark-theme', event.target.checked);
}
}

Choosing the Right Encapsulation Mode​

To help you decide which mode to use, consider the following guidelines:

  1. Use Emulated (default) when:

    • You want component-specific styles
    • You need to support older browsers
    • You're building typical Angular components
  2. Use None when:

    • You want to define global styles within a component
    • You're creating a theme or style library
    • You need to override styles from a third-party library
  3. Use ShadowDom when:

    • You need complete style isolation
    • You're building reusable web components
    • You want to ensure your component will look the same in any context

Performance Considerations​

Each encapsulation mode has different performance implications:

  • Emulated: Small runtime overhead for style scoping
  • None: Best performance as styles are added directly without processing
  • ShadowDom: Can have performance implications for complex components due to shadow boundary traversal

Summary​

Angular's View Encapsulation provides powerful options for controlling how styles are applied in your components:

  • Emulated (default): Scopes styles to the component using attributes
  • None: Applies styles globally without encapsulation
  • ShadowDom: Uses browser's native Shadow DOM for complete isolation

Understanding these modes helps you build components that interact correctly with the rest of your application's styles.

Additional Resources​

Exercises​

  1. Create a simple Angular component with each encapsulation mode and observe how the styles are applied in the browser's developer tools.
  2. Build a card component that uses ViewEncapsulation.Emulated but includes some global styles for the card's header using :host-context().
  3. Create a theme switcher component that applies different themes using ViewEncapsulation.None.
  4. Build a reusable custom form control using ViewEncapsulation.ShadowDom to ensure its styles don't conflict with the parent application.


If you spot any mistakes on this website, please let me know at feedback@compilenrun.com. I’d greatly appreciate your feedback! :)