2HAASStart a project
All posts

Industry Updates

Enhancing Web Components with Declarative Shadow DOM

Learn how Declarative Shadow DOM solves server-side rendering challenges for Web Components, improving SEO and user experience.

The 2HAAS team5 min read

Web Components offer a powerful way to build reusable and encapsulated UI elements for the web. They consist of custom elements, Shadow DOM, and HTML templates. While custom elements provide component lifecycle management and templates offer structure, the Shadow DOM is crucial for maintaining encapsulation, ensuring styles and behavior within a component do not leak out or conflict with the main document.

The challenge with traditional, "imperative" Shadow DOM has always been its reliance on JavaScript. To attach a Shadow DOM to a custom element, you must use JavaScript's attachShadow() method. This means that for server-side rendered (SSR) applications, the content of the Shadow DOM is not available in the initial HTML response. This leads to issues with initial page load performance, search engine optimization (SEO), and accessibility, as crawlers and assistive technologies might not see the complete content until JavaScript executes.

Understanding Declarative Shadow DOM

Declarative Shadow DOM (DSD) is a new web platform feature that allows developers to include Shadow DOM content directly within the initial HTML markup sent from the server. Instead of imperatively creating a Shadow DOM with JavaScript, you can declare it using a standard HTML <template> element with a special shadowrootmode attribute. This small change has profound implications for how Web Components can be used in modern web development.

When a browser parses HTML containing a <template shadowrootmode="open"> (or "closed") element, it automatically detects this and attaches its content as a Shadow DOM to the parent custom element. This process happens at parse time, before any JavaScript runs, making the encapsulated content immediately available to the browser's rendering engine, search engine crawlers, and accessibility tools.

This is a significant departure from the imperative approach:

  • Imperative Shadow DOM: Requires element.attachShadow({ mode: 'open' }) in JavaScript.
  • Declarative Shadow DOM: Defined directly in HTML using <template shadowrootmode="open">.

Benefits of Using Declarative Shadow DOM

Declarative Shadow DOM unlocks several critical advantages for developers and users:

  1. Improved Server-Side Rendering (SSR) and Hydration: Content within the Shadow DOM is immediately available in the initial server response. This eliminates the "flash of unstyled content" (FOUC) and ensures that the component's full structure is visible from the start. Hydration, the process of attaching JavaScript behavior to pre-rendered HTML, becomes more straightforward.
  2. Enhanced SEO: Search engine crawlers can now directly access and index content encapsulated within Shadow DOM, as it's part of the initial HTML payload. This is a massive win for discoverability and ranking of websites using Web Components.
  3. Better Accessibility: Assistive technologies can access the Shadow DOM content immediately without waiting for JavaScript execution. This provides a more inclusive experience for users relying on screen readers and other accessibility tools.
  4. Performance Gains: By reducing the reliance on client-side JavaScript for initial rendering, DSD contributes to faster Core Web Vitals metrics like Largest Contentful Paint (LCP). Users experience quicker perceived page loads and a smoother initial interaction.

Implementing Declarative Shadow DOM in Practice

Implementing DSD involves a simple HTML structure. You wrap the content intended for the Shadow DOM inside a <template> tag, which is nested directly within your custom element. The shadowrootmode attribute specifies whether the Shadow DOM will be open or closed.

Here's a basic example:

<custom-card>
  <template shadowrootmode="open">
    <style>
      :host {
        display: block;
        border: 1px solid #ccc;
        padding: 16px;
        border-radius: 8px;
        box-shadow: 0 2px 4px rgba(0,0,0,0.1);
        background-color: #fffafa;
        color: #1a1a1a;
      }
      h2 {
        color: #cc3f0c;
        margin-top: 0;
      }
      p {
        line-height: 1.5;
      }
      .content {
        margin-top: 10px;
      }
    </style>
    <h2>Card Title</h2>
    <div class="content">
      <p>This is some content inside the custom card component, encapsulated by Declarative Shadow DOM.</p>
      <slot></slot>
    </div>
  </template>
  <div>Default light DOM content for slot.</div>
</custom-card>

<script>
  class CustomCard extends HTMLElement {
    constructor() {
      super();
      // No attachShadow() needed for initial render due to DSD
      // Any client-side logic goes here
    }

    connectedCallback() {
      console.log('CustomCard connected!');
    }
  }
  customElements.define('custom-card', CustomCard);
</script>

In this example, the <template shadowrootmode="open"> block's content (styles and internal HTML) becomes the Shadow DOM for <custom-card> upon initial page load, without requiring attachShadow() in the JavaScript CustomCard class. The <slot></slot> element allows for light DOM content to be projected into the Shadow DOM, providing flexibility.

For browsers that do not yet support Declarative Shadow DOM, a polyfill can be used to ensure consistent behavior. The @webcomponents/template-shadowroot polyfill is a common choice, allowing older browsers to parse the <template shadowrootmode> and manually attach the Shadow DOM using JavaScript.

Browser Compatibility and the Web Standards Landscape

Declarative Shadow DOM is a progressive enhancement. It was initially shipped in Chromium-based browsers (Chrome, Edge, Opera) and is making its way into other browsers. As of early 2024, it is supported by default in Chrome and Edge. Firefox and Safari have expressed positive intent and are working on implementations. This widespread adoption path underscores its importance as a future-proof web standard.

Its inclusion represents a significant milestone for Web Components, making them a more practical choice for a wider range of web applications, especially those that prioritize server-side rendering and initial load performance.

Best Practices for Declarative Shadow DOM

To make the most of DSD, consider these best practices:

  • Always specify shadowrootmode: Clearly define open or closed to indicate how the Shadow DOM interacts with JavaScript.
  • Polyfill for wider support: Use a polyfill to ensure a consistent experience across all modern browsers, even those without native DSD support yet.
  • Progressive Enhancement: Design your custom elements to function gracefully without JavaScript (if possible) for the initial render, then enhance interactivity with JavaScript once loaded.
  • CSS Considerations: Embed necessary styles directly within the <template> for optimal encapsulation and immediate styling.
  • Client-side interactivity: Remember that while DSD handles initial rendering, you will still need JavaScript for dynamic behavior, event handling, and managing state within your custom element.

Conclusion

Declarative Shadow DOM is a game-changer for Web Components. It bridges the gap between the encapsulated power of Shadow DOM and the performance benefits of server-side rendering. By making Web Components more SEO-friendly, accessible, and performant out-of-the-box, DSD empowers developers to build highly robust and efficient web applications. Adopting DSD is a step towards building modern web experiences that are fast, accessible, and resilient.

Keep reading

  1. Industry Updates2 min read

    Web interoperability: what the Interop project fixes

    The Interop project gets Chrome, Firefox, Safari and Edge to agree on how web features behave. Here is how it works and what it changes for everyday frontend work.

  2. Industry Updates4 min read

    WASI for Server-Side: WebAssembly Outside the Browser

    Learn how WebAssembly System Interface (WASI) enables secure, portable, and fast execution of code outside the browser for server-side and edge computing.

  3. Industry Updates4 min read

    Seamless Cross-Language Modules with WebAssembly Components

    Learn how the WebAssembly Component Model enables seamless, language-agnostic code integration. Discover how to build and leverage universal modules for modern applications.

Tell us what’s in the way.

A few lines is enough. You get a candid yes, a candid no, or a sharper question.

  • Working software in week four
  • A reply in one business day
  • Handover is the deliverable

Prefer email? [email protected]

The problem, who it is for, and what is in the way right now.

One business day to a real reply. No sales sequence, no call required.