Problem
When an SSR/SSG engine renders nested Custom Elements via Declarative Shadow DOM (DSD), it must resolve components in topological order. If <my-card> renders <my-button> in its shadow DOM, <my-button> must be registered before <my-card> — otherwise the nested element is silently skipped by the SSR engine, producing broken output.
CEM currently covers the component's API surface (attributes, events, slots, CSS parts) but has no field for render-time dependencies. Today, SSR engines have three bad options:
- Runtime trial-and-error — render a component, scan the output for nested CE tags, then recurse. Wasteful (O(n·d) where d = nesting depth) and doesn't support pre-render optimization.
- Manual per-project configuration — not portable, defeats the purpose of a machine-readable manifest.
- Invent a parallel format — each framework builds its own dependency graph, losing CEM's interop promise.
This gap affects any framework doing WC SSR via DSD: LessJS, Astro, Enhance, Svelte, etc.
Proposal
Add x-render-dependencies: string[] — an array of Custom Element tag names that must be registered before this component can be correctly rendered.
{
"kind": "class",
"name": "MyCard",
"tagName": "my-card",
"x-render-dependencies": ["my-button", "my-icon"]
}
This tells an SSR engine: "Before you render <my-card>, make sure <my-button> and <my-icon> are registered — they appear in its shadow DOM."
Auto-detection
This field is auto-detectable by the analyzer. Scanning the component's render() output HTML for custom element tag names is reliable:
<([a-z][a-z0-9]*-[a-z0-9-]+)[\s>\/]
For Lit components, the analyzer can also inspect static styles and template literals. For vanilla components, innerHTML assignments can be scanned.
Working Implementation
I'm building LessJS, a Deno-first WC SSG framework. We've been solving this problem in production with a bottom-up recursive renderer:
render-nested.ts — recursively processes CE tags bottom-up using parse5 AST, renders DSD for each, and preserves light DOM for slots.
render-dsd.ts — core DSD renderer that supports nested CE resolution.
- Three-layer component model:
dsd-static (no hydration) / dsd-interactive (DSD + event hydration) / pure-island (client-only).
Having x-render-dependencies in CEM would allow us to build the dependency graph before rendering instead of discovering it at runtime. This enables:
- Pre-render optimization: sort components topologically once, render in one pass
- Tree-shaking: only register components that are actually needed for a given page
- Registry hubs: a WC registry can auto-resolve dependency chains
Related Fields (for discussion)
While x-render-dependencies is the highest-value single field, there are related SSR metadata gaps that could be addressed together or in follow-up proposals:
| Field |
Type |
What it solves |
x-layer |
"server-only" | "server-hydratable" | "client-only" |
Whether the component can be SSR'd and/or hydrated |
x-shadow |
{ mode, clonable?, delegatesFocus?, serializable?, slotAssignment? } |
DSD template attributes needed for SSR output |
x-renderer |
"lit" | "vanilla" | "generic" |
Which template rendering backend the component requires |
I'm intentionally scoping this issue to x-render-dependencies only — it's the smallest consensus point with the clearest value. The other fields can be separate proposals if there's interest.
Open Questions
- Should
x-render-dependencies include transitive dependencies, or only direct ones? (I'd suggest direct-only — the SSR engine can resolve transitivity from the graph.)
- Should the analyzer auto-detect this field, or should it be manually declared? (I'd suggest auto-detect with manual override.)
- Is
x-render-dependencies the right name, or is x-dependencies / x-render-deps clearer?
Problem
When an SSR/SSG engine renders nested Custom Elements via Declarative Shadow DOM (DSD), it must resolve components in topological order. If
<my-card>renders<my-button>in its shadow DOM,<my-button>must be registered before<my-card>— otherwise the nested element is silently skipped by the SSR engine, producing broken output.CEM currently covers the component's API surface (attributes, events, slots, CSS parts) but has no field for render-time dependencies. Today, SSR engines have three bad options:
This gap affects any framework doing WC SSR via DSD: LessJS, Astro, Enhance, Svelte, etc.
Proposal
Add
x-render-dependencies: string[]— an array of Custom Element tag names that must be registered before this component can be correctly rendered.{ "kind": "class", "name": "MyCard", "tagName": "my-card", "x-render-dependencies": ["my-button", "my-icon"] }This tells an SSR engine: "Before you render
<my-card>, make sure<my-button>and<my-icon>are registered — they appear in its shadow DOM."Auto-detection
This field is auto-detectable by the analyzer. Scanning the component's
render()output HTML for custom element tag names is reliable:For Lit components, the analyzer can also inspect
static stylesand template literals. For vanilla components,innerHTMLassignments can be scanned.Working Implementation
I'm building LessJS, a Deno-first WC SSG framework. We've been solving this problem in production with a bottom-up recursive renderer:
render-nested.ts— recursively processes CE tags bottom-up using parse5 AST, renders DSD for each, and preserves light DOM for slots.render-dsd.ts— core DSD renderer that supports nested CE resolution.dsd-static(no hydration) /dsd-interactive(DSD + event hydration) /pure-island(client-only).Having
x-render-dependenciesin CEM would allow us to build the dependency graph before rendering instead of discovering it at runtime. This enables:Related Fields (for discussion)
While
x-render-dependenciesis the highest-value single field, there are related SSR metadata gaps that could be addressed together or in follow-up proposals:x-layer"server-only" | "server-hydratable" | "client-only"x-shadow{ mode, clonable?, delegatesFocus?, serializable?, slotAssignment? }x-renderer"lit" | "vanilla" | "generic"I'm intentionally scoping this issue to
x-render-dependenciesonly — it's the smallest consensus point with the clearest value. The other fields can be separate proposals if there's interest.Open Questions
x-render-dependenciesinclude transitive dependencies, or only direct ones? (I'd suggest direct-only — the SSR engine can resolve transitivity from the graph.)x-render-dependenciesthe right name, or isx-dependencies/x-render-depsclearer?