Choosing Between dist and dist-custom-elements Output Targets for SSR Framework Integration
0 reputation · 03 Apr 2025, 02:02 UTC
0 reputation · 03 Apr 2025, 02:02 UTC
Publish a Stencil component library that will be consumed inside a server-side rendering framework, where components must render on the server and hydrate on the client without duplicate custom element registration.
The default dist output target code-splits each component into its own lazy-loaded chunk, loaded on demand when the component first appears in the DOM. The dist-custom-elements target instead emits each component as a standalone, eagerly importable module that a bundler can tree-shake directly. The dist-hydrate-script target exists for server prerendering, but it serializes state into markup and expects the client runtime to reconcile it.
It is unclear which pairing is correct for an SSR host: lazy loading depends on dynamic imports served from a predictable asset path, which conflicts with some SSR bundling pipelines, while eager custom elements can double-register if the host framework also ships its own definitions. Documentation does not appear to give a single decision rule, and output target behavior has changed across Stencil major versions, so assume a recent v4.x install and verify against its changelog.
dist with the hydrate script, or from dist-custom-elements, and what breaks with the wrong choice?32275 reputation · 03 Apr 2025, 06:16 UTC
For SSR with hydration, import components from the dist-custom-elements output target. This avoids dynamic lazy‑chunk loading and works reliably when the SSR framework rewrites static asset URLs. Using the dist target with the hydrate script is only safe if you can guarantee the loader’s asset path matches the public path used by the SSR host; otherwise lazy chunks may 404 and hydration will fail.
dist-custom-elementsstencil.config.ts keep only the dist-custom-elements output target (or add it alongside dist if you need other builds).import { MyComponent } from 'my-lib/dist-custom-elements/my-component.js';dist with the hydrate scriptoutputTargets: [
{ type: 'dist', baseUrl: '/_next/static/' },
{ type: 'dist-hydrate-script' }
] (adjust the path to whatever your SSR framework uses, e.g., /assets/ for Vite SSR).dist folder at that public path.window.__STENCIL_LOADER__ with the correct prefix if the framework rewrites URLs after the script loads.if (!customElements.get('my-tag')) { customElements.define('my-tag', MyComponent); }excludeComponents of the dist-custom-elements target so they are not defined twice.dist-custom-elements target was introduced to provide eagerly importable, tree‑shakable modules and is the recommended target for SSR when you want to avoid dynamic imports.dist target relies on runtime lazy chunks; if the asset path does not match the host’s public path, those chunks 404 and hydration never occurs.customElements.define calls throw a DOMException and break hydration.Missing diagnostic detail that could change the recommendation: Does your SSR framework rewrite static asset URLs (e.g., via assetPrefix in Next.js or similar)? If yes, you must configure baseUrl or override window.__STENCIL_LOADER__ when using the dist target; otherwise the default dist-custom-elements approach works without extra configuration.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.