Diagnosing Lazy‑Loaded Component Failures in StencilJS Production Builds
When a StencilJS app’s lazy‑loaded components fail in production, the root causes often lie in build config, base href, or server rewrite rules. This diagnostic guide walks through symptoms, checks, fixes, and escalation steps to help you pinpoint and resolve the issue quickly.
30 Dec 2025, 21:42 UTC

Problem Statement
When a StencilJS application is deployed to production, developers sometimes notice that lazy‑loaded components either never render, throw runtime errors, or cause route navigation to break. These symptoms often indicate that the dynamic import mechanism or the server configuration is mis‑aligned.
Recognizable Symptoms
- Component does not appear and the Network tab shows a
404 Not Foundfor the chunk file (e.g.,/components/my-component/my-component.js). - Component appears but immediately throws a JavaScript error such as
Failed to load moduleorCannot find module. - After navigating to a new SPA route, the page reloads or shows a blank state, indicating that the router failed to resolve the component.
- Console logs show
Failed to load moduleorUncaught SyntaxError: Unexpected tokenwhen a lazy component is requested. - Build output contains static imports instead of dynamic
import()calls for components marked withlazy: true.
Cause & Diagnostic Table
| Symptom | Likely Cause | Diagnostic Check |
|---|---|---|
| 404 on chunk file | Chunk not emitted or server not serving it | Check dist/esm for the file and verify server static mapping. |
| Runtime "Failed to load module" | Base href mismatch or wrong import path | Inspect <base href="/"> and the URL used in import(). |
| Route navigation breaks | SPA rewrite rules missing or misconfigured | Test navigation on a minimal static server (e.g., serve -s dist). |
| Static imports in build output | Lazy loading disabled in stencil.config.ts | Review outputTargets and build.lazy settings. |
| Console “Failed to load module” after CDN cache | Cache headers prevent fetching new chunks | Check Cache-Control headers for chunk URLs. |
Ordered Verification Steps
- Inspect Network – Open the browser’s Network panel, trigger the lazy component (e.g., click a button that loads it), and look for a
404on the chunk file. If present, skip to step 4. - Check Base Href – In
index.html, ensure<base href="/">matches the root path served by your web server. A mismatched base href rewrites the chunk URL incorrectly. - Validate Build Output – In
dist/esmordist/esm-stencil, open the generated JavaScript for a lazy component. Search for a dynamic import pattern such asimport('./my-component.js'). If it’s a staticrequireorimportstatement withoutimport(), lazy loading is disabled. - Review
stencil.config.ts– Confirm thatoutputTargetsincludes{ type: 'dist', lazy: true }and thatbuild: { lazy: true }is not overridden elsewhere. Example snippet:export const config: Config = { namespace: 'myApp', outputTargets: [ { type: 'dist', lazy: true, esmOutput: true, }, ], build: { lazy: true, }, }; - Test with Minimal Static Server – Run
serve -s dist(requiresnpm i -g serve) and navigate through the app. If lazy components load correctly, the issue is likely server‑specific. - Examine Server Rewrite Rules – For SPA deployments behind Nginx, Apache, or a CDN, ensure that all routes fallback to
/index.html. Example Nginx snippet:location / { try_files $uri $uri/ /index.html =404; } - Check CDN Cache Headers – Verify that
Cache-Controlfor chunk files allows fetching updated versions. A header likeCache-Control: max-age=31536000, immutablecan block new chunks after a deploy.
Targeted Fixes
- Re‑emit Missing Chunks – If the chunk file is absent, rebuild with
stencil build --prodand confirm thatlazy: trueis set. Avoid disabling lazy loading in production. - Correct Base Href – Update
index.htmlto match the deployment root. For sub‑directory deployments, set<base href="/myapp/">and adjust dynamic import URLs accordingly. - Enable Lazy Loading in Build Config – Add
lazy: trueto theoutputTargetsarray and remove any globalbuild.lazy = falseoverrides. - Adjust Server Rewrite Rules – Add a fallback to
/index.htmlfor all non‑static paths. For CDN edge functions, ensure that the origin fetches the correct chunk URLs. - Relax CDN Cache for Chunks – Set
Cache-Control: no-cache, must-revalidatefor/components/*/*.jsor use a separate CDN path with short TTL.
Escalation Criteria
- If after following the above steps the component still fails to load, verify that the component’s
@Componentdecorator includeslazy: trueand that the file path matches the import statement. - Check the browser console for stack traces pointing to a missing module. If the error shows a path that does not exist on the server, the build output may be corrupted; consider cleaning the
distfolder (rimraf dist && stencil build --prod). - When the issue persists across multiple environments (local dev, staging, production), open a ticket with your CI/CD pipeline team to ensure that the build artifacts are correctly uploaded to the artifact store and that the deployment step does not strip dynamic imports.
- If the lazy component contains third‑party modules that are not bundled (e.g., external ES modules), ensure that the bundler preserves them. Stencil’s default Rollup config may need
externaloverrides to keep dynamic imports intact.
Quick Reference Cheat‑Sheet
- Build command:
stencil build --prod - Minimal server test:
serve -s dist - Dynamic import pattern:
import('./my-component.js') - Server rewrite (Nginx):
try_files $uri $uri/ /index.html =404; - Cache header for chunks:
Cache-Control: no-cache, must-revalidate
Conclusion
Lazy‑loaded components in StencilJS rely on both the build configuration and the host environment. By following the ordered verification steps and applying the targeted fixes above, most production deployment issues can be resolved quickly. If problems persist, the escalation path ensures that configuration drift or CI/CD pipeline errors are surfaced to the appropriate team.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.