It lurks in the shadows…creating hidden trees…playing by a set of its own rules…it’s…

SHADOW DOM!

But, what exactly is Shadow DOM? Put simply, Shadow DOM is an API that is part of the Web Component model. It helps to isolate component internals, protecting users from breaking changes and global CSS.

I’d like to think we here at Ionic know a thing or two about Shadow DOM, as we recently made the move to using it in Ionic 4, and with that, received a lot of questions from the community on just what the heck it is. There’s a lot of information (and misinformation) floating around about Shadow DOM, so let’s go through the API and see what it actually does.

What is Shadow DOM?

Shadow DOM sounds like it could be a complicated API, but it’s actually built on two simple ideas, isolation and location. Need to create a bit of DOM that is isolated from the global scope? Shadow DOM is here to help. Need to specify the exact location of a piece of DOM? Shadow DOMs scope API is what you need!

It can be helpful to think of components that use Shadow DOM as modules for HTML. Markup and styles are isolated to their own DOM tree, and removed from the global context. This enables us to unlock some pretty incredible benefits:

  • Isolated DOM
  • Isolated CSS
  • Simplified CSS Rules

Typically, Shadow DOM is associated with Custom Elements, but you can still use Shadow DOM with regular HTML elements. In fact, most browsers use Shadow DOM without you even noticing. For example, every time you use video tag, you’re actually using an element that has Shadow DOM. Think about it, devs just need to write the <video> element, supply a video source, and set a few attributes on the HTML.

<video src="https://some-video.mp4" controls></video>

But parts of the video element, like video controls, are automatically created. Developers don’t actually have to provide their own play button, progress bar, volume control, they’re automatically provided. This is Shadow DOM in action!

Now let’s use this bit of Ionic markup:

<ion-content>
  <ion-item>
    Hello World
  </ion-item>
  <ion-button>
    I'm a button
  </ion-button>
</ion-content>

This markup is pretty simple, all we’re doing is rendering an item and a button. Nothing too special right? Well, let’s look at the dev tools

Woah, where did all this come from? This is Shadow DOM working in Ionic’s components. All the internals of ion-item and ion-button can be abstracted away from the user, meaning all they need to know is the tag name, and is automatically handled by the browser. In short…it’s magic!

For Slots, we can provide all the entry points up front. For example, ion-item has three slots, Start, End, and the default. By using Shadow DOM, we can keep their location right where we want them to be, vs having users write their markup in a specific order. So there could be an item written as:

  <ion-item>
    <ion-icon name="home" slot="end"></ion-icon>
    Hello World
  </ion-item>

And Shadow DOM and slots would know how to correctly render this.

Ok, so what’s the big deal? Ionic v3 already did something similar using Angular’s Content Projection. While Angular’s Content Projection is available, V4’s approach uses standardized APIs that all browser vendors have agreed on. Meaning, there’s no extra code needed at run time. By using the Shadow DOM, we get all the benefits of content projection, but built into the browser.

From a component author’s perspective, we can now author Ionic’s components in a way to prevents most breaking changes. Since we can control where and how a component’s content can be rendered, we’re able to prevent many mistakes and harden user’s code.

With the component’s internal markup isolated, updates and improvements can be made to any component, without the end user needing to update their code.

This is the real reason why Shadow DOM is being used in Ionic.

Parting Words

While Shadow DOM has been around for a few years now, it’s reached the point where we can finally take advantage of it in modern browsers. For the older browsers that are still around, we got them covered too, with on-the-fly polyfills being loaded. If a browser doesn’t support native Shadow DOM, we can polyfill it on-demand. However, if a browser already has native support, Ionic never downloads and parses any unnecessary polyfill JavaScript, but rather just uses the built in API.

It’s one of the many new exciting features under the hood in Ionic 4, and makes maintaining the Framework much easier. And as a result, we’ve been able to drastically reduce the amount of CSS code required for users’ apps because of its simplicity. Less code === Faster apps.

On top of that, devs building with Ionic don’t need to worry about potential breaking changes. Smaller CSS that is easier to maintain, and markup that has fewer breaking changes… what’s not to love about Shadow DOM?!

In a future post, we’ll look at how theming is done in V4 now that all of Ionic’s styles are isolated…

Spoiler, it’s CSS Variables 😏

Signup for the Ionic Newsletter to get the latest news and updates!

  • http://janpiotrowski.de/ Jan Piotrowski

    I don’t get the “Slot” paragraph. What are slots? What does the code example there do?

    • Mike Hartington

      Slots are basically Angular’s way of doing content projection or the older transclusion API. You can keep a “slot” or spot in a components HTML open and let the users say what should go in there. Given this example

      https://gist.github.com/mhartington/0778298a63fb1b476ac9a5627ea327a1

      We have a default slot (one with no name), and a named slot (start). By doing this, we can pass content into the component with the default slot, and render other content in the “start” slot, but have it work in any order.

      Also, take a look at the MDN docs as well
      https://developer.mozilla.org/en-US/docs/Web/API/Element/slot

      • http://janpiotrowski.de/ Jan Piotrowski

        So this is a new thing for people coming from Ionic v3, and is one of the useful features web components provide via Shadow DOM?

        • Ian Luca

          Not a new thing, because angular already had it, and ionic v3 was using it. Difference is that ionic v4 uses the native implementation

          • Mike Hartington

            Yeah, it was already around in V3, when ever you did `item-start` or `item-end` for ion-items. The main point is that instead of using attribute selectors, the native slot API can be used, and devs do not needed to know about the details happening under the hood

    • RhysC

      Think of slots as props but for web components. Its just a way of passing arguments to a component.

      • Ian Luca

        It’s not arguments, its html tags that will be rendered inside the component somewhere.
        Props/attributes are the way to pass arguments, like booleans, objects, arrays, etc.

      • https://plus.google.com/112893238403471145260 Yogesh Patil

        Its not an arguments.Its like a web component that we actually use in HTML.
        Make sure you define proper slot type.

    • Flawyte

      Suppose you’re creating a <page-layout> component, and that you want to be able to pass to this component a footer element, a header element and a content element, and having them automatically laid out with the header at the page’s start and the footer at its end. This what using your component would look like at first :


      <page-layout>
      <div class="Header"></div>
      <main></main>
      <footer></footer>
      </page-layout>

      Now the question is: How do you tell your <page-layout> element which child is the header, which is the footer and which is the page’s content? The answer is <slot>-s :


      <page-layout>
      <header slot="header"></header>
      <main slot="content"></main>
      <footer slot="footer"></footer>
      </page-layout>

      and in your component’s DOM :


      <template>
      <div style="display: flex; flex-direction: column;">
      <slot name="header"></slot>
      <slot name="content"></slot>
      <slot name="footer"></slot>
      </div>
      </template>

      This article on Google Developers might help too : https://developers.google.com/web/fundamentals/web-components/shadowdom#composition_slot

  • Davo

    Thx for the article 🙂 I don’t get something regarding isolated styles and CSP. May I ask then, is it correct, using shadow-dom I will have to add a `sha-….` value in the style-src rules of my CSP for each and every single isolated component I’ve built with a custom style, is that correct? does that makes sense?

    I’ve got like 100 pages/components, that would be kind of a job…

    Thx in advance for any feedbacks
    David

    P.S.: Same question there https://forum.ionicframework.com/t/shadow-dom-and-csp-style-src

    • Mike Hartington

      Replied on the forum! TLDR; you should be fine with just one setting on the index.html

  • Adrien

    Hello @mhartington:disqus thanks for the article,

    I just tried V4 and while it’s awesome if i’m correct for the menu I cannot juste override .menu-inner class via CSS ?
    The question is then, how do we change the background color of the menu background ?

    Thanks

    • mongedecristo

      then CSS: #menu-content { background-color: map-get($colors, baseColorNameYouSetInVariablesScss); } That’s how easy it is to change the background color of a side menu.

      • Adrien

        I’ll try that, and thanks for the help

  • dorothy

    If you ever use the shadow dom then you will know how good it is and how perfect it could be if you can use it in the right way..

  • Bassam Habash

    Thanks for awesome article, but I have a problem in editing the css style for a shadow root dom.

    example: I want to change the background color , how can I do that ?

  • http://www.badpenguin.org/ Antonio Gallo

    How can i access shadowRoot of Ionic 4 components? In ngAfterViewInit its empty
    Is IonViewDidEnter the right callback?
    Or is there a “ready” promise available on the ionic component itself?