StencilJS production build omits lazy-loaded chunks referenced in chunk manifest
0 reputation · 03 May 2021, 08:39 UTC
0 reputation · 03 May 2021, 08:39 UTC
Achieve a reliable production deployment where all lazy-loaded components defined in the StencilJS chunk manifest are emitted and load correctly at runtime.
The compiler's static analysis cannot resolve dynamic import expressions that use variables (for example import("./components/" + name)), so those chunks are not emitted in a production build even though the development server compiles them on demand. The generated stencil-chunk-manifest.json still references the missing chunks, causing 404 errors when the hydration script requests them. Output target configuration adds another variable: the www target uses content-hashed filenames while dist-custom-elements uses deterministic names, so mixing targets without aligned basePath and buildDir settings produces manifest mismatches. Stencil v4's migration to Vite also changed the chunk generation pipeline, and legacy Rollup plugins may emit manifests that the new runtime cannot consume.
basePath and buildDir be coordinated across multiple outputTargets to guarantee a single, consistent manifest at deploy time?The short answer: your chunks are missing because the Stencil compiler only emits what it can trace statically. Any lazy-loading path built from a variable — import("./components/" + name) — is invisible to the build, while the manifest still records the component as lazy-loadable, so the runtime hydration script requests files that were never written to disk. The fix is to give the compiler a static enumeration of every component, and to keep one coherent output target configuration so the manifest and the emitted files agree.
Replace variable-based dynamic imports with a static registry the compiler can follow. The reliable pattern is an explicit map of literal import expressions:
const loaders = {
'my-button': () => import('./components/my-button/my-button.js'),
'my-card': () => import('./components/my-card/my-card.js'),
// one literal entry per lazy component
};
export function loadComponent(tag) {
const loader = loaders[tag];
return loader ? loader() : Promise.reject(new Error(`Unknown component: ${tag}`));
}
Each string literal is resolvable at build time, so every referenced module is split into a chunk and emitted. If you generate this map, generate it as source code with literal paths — not as runtime string concatenation. Note that in a standard Stencil project the component loader is generated for you from @Component metadata; this problem typically appears when you add a custom lazy-loading layer on top. If that describes your setup, prefer Stencil's own lazy loading and drop the custom indirection where possible.
The manifest mismatch comes from two targets writing different naming conventions into overlapping locations. Keep them separated and aligned:
buildDir (for example www/build for the www target and dist/custom-elements for dist-custom-elements). Never point two targets at the same directory.basePath to the exact public URL prefix where the assets will be served — the URL path, not a filesystem path. If the app is deployed under /app/, the manifest and chunk requests must resolve under that prefix.After building, verify consistency directly: open stencil-chunk-manifest.json and confirm every chunk it lists exists as a physical file in the same output tree, then load the page and check the network tab — the requested chunk URLs should match your configured basePath exactly. A 404 on a hashed filename almost always means a stale manifest or a basePath mismatch, not a missing build step.
On current Stencil versions the bundling pipeline differs from the legacy Rollup-based one, and third-party Rollup plugins that inspect or rewrite chunk output can produce manifests the runtime loader cannot consume. The conservative steps:
@stencil/core to an exact version in package.json (no caret) and commit the lockfile so CI and local builds are identical.stencil.config.ts for custom Rollup plugins that touch output filenames or manifests; remove or replace any that were written for pre-Vite Stencil. Test this by running a clean production build and diffing the manifest against the emitted files.One caveat worth flagging: the exact chunk-naming and manifest behavior varies across Stencil 4.x minor releases, so treat the verification steps above (manifest-to-file diff plus a network-tab check) as the source of truth for your pinned version rather than any general claim about naming conventions. If chunks still 404 after the registry and target cleanup, the one diagnostic detail that would change the recommendation is whether the missing chunk names appear in the manifest with hashes that match files on disk — that distinguishes a static-analysis gap from a deployment path mismatch.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.