You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
NotSupportedError: Failed to execute 'createElement' on 'Document': The result must not have attributes when using media-chrome React wrappers with Next.js 16 #8
Opening this example through a link or pressing unmount and mount will log this message.
Uncaught NotSupportedError: Failed to execute 'createElement' on 'Document': The result must not have attributes
Reloading that same page does not trigger the error.
Note
Disclaimer: Info created with Claude
Background
ce-la-react's createComponent has an SSR path that generates <template shadowrootmode="open"> as a light DOM child of each custom element during server rendering:
// ce-la-react/dist/ce-la-react.js
if (typeof window === "undefined" && elementClass?.getTemplateHTML && elementClass?.shadowRootOptions) {
// adds <template shadowrootmode="open"> as first child
reactProps.children = [templateShadowRoot, reactProps.children];
}
This worked correctly in Next.js ≤15 because the browser's native HTML parser consumed <template shadowrootmode> as Declarative Shadow DOM before React hydrated — so by the time React walked the DOM, the template was gone and the light DOM matched the client vDOM.
What broke in Next.js 16
Next.js 16 delivers some page content via JavaScript insertion (streaming/RSC payloads) rather than through the initial HTML parse. Content inserted via JS (e.g. innerHTML, insertAdjacentHTML) is not processed as Declarative Shadow DOM — the <template shadowrootmode> stays in the light DOM as a regular element.
This creates a mismatch:
DOM: <media-controller> has <template> as a light child (not consumed)
Client vDOM: no <template> (ce-la-react skips it when typeof window !== "undefined")
React 19 falls back to client-side re-rendering of the mismatched subtree, calling document.createElement('media-controller') etc. programmatically. Per the Custom Elements spec, if a constructor sets any attributes before document.createElement returns, the browser throws NotSupportedError. This kills the render.
Proposed Fix (ce-la-react)
The typeof window === "undefined" guard is the wrong condition to use. The DSD SSR template only helps when HTML is delivered via the native parser. When it's not, it causes breakage.
Option A (safest): Remove the DSD SSR template path entirely. All media-chrome elements already guard against this in their constructors with if (!this.shadowRoot) { attachShadow(); ... }, so they degrade gracefully.
Option B: Expose a disableDSD or ssr option in createComponent so callers can opt out.
Reproduction example: Open the latest media-chrome NextJS example for React Wrappers
https://media-chrome-demo-nextjs-62k8eebf8-mux.vercel.app/react-wrappers
Opening this example through a link or pressing unmount and mount will log this message.
Reloading that same page does not trigger the error.
Note
Disclaimer: Info created with Claude
Background
ce-la-react's
createComponenthas an SSR path that generates<template shadowrootmode="open">as a light DOM child of each custom element during server rendering:This worked correctly in Next.js ≤15 because the browser's native HTML parser consumed
<template shadowrootmode>as Declarative Shadow DOM before React hydrated — so by the time React walked the DOM, the template was gone and the light DOM matched the client vDOM.What broke in Next.js 16
Next.js 16 delivers some page content via JavaScript insertion (streaming/RSC payloads) rather than through the initial HTML parse. Content inserted via JS (e.g.
innerHTML,insertAdjacentHTML) is not processed as Declarative Shadow DOM — the<template shadowrootmode>stays in the light DOM as a regular element.This creates a mismatch:
DOM:
<media-controller>has<template>as a light child (not consumed)Client vDOM: no
<template>(ce-la-react skips it whentypeof window !== "undefined")React 19 falls back to client-side re-rendering of the mismatched subtree, calling
document.createElement('media-controller')etc. programmatically. Per the Custom Elements spec, if a constructor sets any attributes beforedocument.createElementreturns, the browser throwsNotSupportedError. This kills the render.Proposed Fix (ce-la-react)
The
typeof window === "undefined"guard is the wrong condition to use. The DSD SSR template only helps when HTML is delivered via the native parser. When it's not, it causes breakage.Option A (safest): Remove the DSD SSR template path entirely. All media-chrome elements already guard against this in their constructors with
if (!this.shadowRoot) { attachShadow(); ... }, so they degrade gracefully.Option B: Expose a
disableDSDorssroption increateComponentso callers can opt out.Affected versions
ce-la-react: 0.3.2 (latest)
next: ~16.1.1
react: 19.2.2