Shadow Root

A reusable widget can require style isolation so its styles do not affect the surrounding document and document styles do not affect the widget.

The browser’s Shadow DOM provides this isolation with a separate DOM tree. Retend exposes it through the <ShadowRoot> component.

Using ShadowRoot

Wrap the content to isolate in <ShadowRoot>. Import it from retend-web, because it is browser-specific.

import { ShadowRoot } from 'retend-web';

function Card() {
  return (
    <div class="card-container">
      <ShadowRoot>
        <p>This content lives inside an isolated shadow DOM!</p>
      </ShadowRoot>
    </div>
  );
}

<ShadowRoot> attaches to its parent <div>. Content inside it is isolated from the surrounding document.

Escaping Global Styles

The Shadow DOM isolates the elements it contains, so a <style> element inside the shadow root applies within that shadow root rather than to the rest of the document.

import { ShadowRoot } from 'retend-web';

function StyledComponent() {
  return (
    <div>
      <div class="test">I turn blue because of the global stylesheet.</div>

      <ShadowRoot>
        {/* This style ONLY applies inside this ShadowRoot */}
        <style>{'.test { color: red; }'}</style>

        <div class="test">I am completely isolated and my text is red.</div>
      </ShadowRoot>
    </div>
  );
}

The global .test class does not affect the inner div, and the inner <style> does not change the outer div.

Reactivity and Features

The HTML is isolated from the rest of the page, but Retend’s component behavior remains available across the shadow boundary:

  • Reactivity: You can use Cells and control flow functions (If, For) naturally.
  • Context and Scopes: Context from createScope() flows down through the shadow boundary perfectly.
  • Events: Event listeners like onClick work right out of the box.
import { Cell } from 'retend';
import { ShadowRoot } from 'retend-web';

function IsolatedToggle() {
  const isOn = Cell.source(false);

  return (
    <div class="toggle-host">
      <ShadowRoot>
        <style>
          {`
            button {
              padding: 0.5rem 1rem;
              background-color: #007bff;
              color: white;
              border-radius: 4px;
            }
          `}
        </style>

        <p>Status: {isOn}</p>
        <button type="button" onClick={() => isOn.set(!isOn.get())}>
          Toggle
        </button>
      </ShadowRoot>
    </div>
  );
}

Important Rules

When using <ShadowRoot>, follow these rules:

  1. It must be inside an HTML element: The <ShadowRoot> attaches to its direct parent. That parent must be a standard HTML element (like a <div>, <section>, or <article>).
  2. Only one per parent: You should never place two <ShadowRoot> components directly inside the exact same parent element.