Skip to content

Bug: Duplicate <style> tags per component instance with serializeShadowRoot: 'scoped' in Next.js runtime SSR #773

@Armand-Lluka

Description

@Armand-Lluka

Bug Description

When using serializeShadowRoot: "scoped" with the Next.js runtime-based SSR approach, each component instance renders with duplicate <style> tags in the server-rendered HTML. This leads to unnecessary bloat in the SSR output and potential performance issues.

Reproduction

Setup

  • Next.js 15 with React 19
  • @stencil/react-output-target with runtime-based SSR
  • Stencil component with shadow: true and scoped styles
  • Using serializeShadowRoot: "scoped" option

Steps to Reproduce

  1. Create a Stencil component with shadow DOM and styles (e.g., my-counter)
  2. Set up Next.js runtime SSR with serializeShadowRoot: "scoped"
  3. Render multiple instances of the same component on a page
  4. View the SSR'd HTML source

Demo Code

A demonstration page has been created in the local branch demo/scoped-style-duplication-bug:

  • File: example-project/next-15-runtime-based/src/app/dedup-test/page.tsx
  • Renders: 5 instances of <MyCounter /> component
import { MyCounter } from 'component-library-react/next';

export default function DedupTest() {
  return (
    <div style={{ padding: '2rem' }}>
      <h1>Style Deduplication Test</h1>
      <p>This page renders 5 instances of MyCounter to demonstrate style deduplication.</p>
      
      <div style={{ display: 'flex', flexDirection: 'column', gap: '1rem' }}>
        <MyCounter />
        <MyCounter />
        <MyCounter />
        <MyCounter />
        <MyCounter />
      </div>
    </div>
  );
}

Expected Behavior

When rendering 5 instances of MyCounter, the scoped styles should be deduplicated:

  • Expected: Single <style> tag (or shared style component) for all instances
  • Expected: Lightweight instance wrappers that reference the shared styles

Actual Behavior

Currently, each component instance gets its own duplicate <style> tag:

  • Actual: 5 instances = 5 identical <style> tags
  • Actual: Each instance wrapper includes the full CSS

Performance Issues

  • HTML Bloat: With N instances, styles are duplicated N times
  • Bandwidth: Larger SSR payloads, especially for components with extensive styles
  • Parse Time: Browser has to parse duplicate style blocks
  • Memory: More DOM nodes than necessary

Environment

  • Package: @stencil/react-output-target
  • Version: Latest (main branch)
  • Framework: Next.js 15 + React 19
  • SSR Mode: Runtime-based with serializeShadowRoot: "scoped"

Verification

To verify this bug:

  1. Checkout the demo branch: git checkout demo/scoped-style-duplication-bug
  2. Build the component library: cd example-project/component-library && npm run build
  3. Build the React library: cd ../component-library-react && npm run build
  4. Run Next.js dev server: cd ../next-15-runtime-based && npm run dev
  5. Navigate to /dedup-test
  6. View page source (disable JavaScript to see pure SSR output)
  7. Search for <style> tags - you'll find 5 duplicate blocks

Metadata

Metadata

Assignees

No one assigned

    Labels

    needs reproductiona code reproduction is needed from the issue authortype: bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions