Implementing StencilJS Lazy‑Loading for Custom Components: A Practical Guide
Learn how to enable lazy‑loading for a StencilJS component, reduce bundle size, and verify the change. Follow this step‑by‑step guide to add a <component lazy> tag, provide a fallback, and confirm the chunk is fetched at runtime.
01 Aug 2026, 22:24 UTC

Desired Outcome
Reduce the size of the first‑paint bundle by loading a non‑critical custom component only when it is needed. The goal is to keep the initial JavaScript payload small while still providing a seamless user experience when the component is requested.
Prerequisites
- Node.js 18+ and npm or yarn installed.
- StencilJS project generated with Stencil 2.0 or newer (check
package.jsonfor"@stencil/core": "^2.0.0"). - Component exported as a module, e.g.,
src/components/my-component/my-component.tsxwithexport class MyComponent. - Optional: SSR setup if you plan to render on the server.
Focused Procedure
- Mark the component as lazy‑loadable. In Stencil, add the
lazyattribute to the element tag in the consumer code:
This tells the compiler to treat<my-component lazy></my-component>my-componentas a lazy chunk. - Provide a fallback UI. The component will render a placeholder until the JavaScript is fetched. Add a
<slot name="fallback">inside the component’s render method or use a simple<div>Loading…</div>that will appear while the module is loading.export class MyComponent { render() { return ( <div> <slot name="fallback"><div>Loading component…</div></slot> <!-- real content will be inserted after load --> </div> ); } } - Dynamic import fallback (optional). If you prefer to control the import manually, replace the
lazyattribute with a call toimport()inside the component’scomponentWillLoadlifecycle hook:
This approach gives you fine‑grained control but requires the component to be exported as a separate module.componentWillLoad() { import('./my-component').then(() => { // module loaded }); } - Build and verify chunk creation. Run:
Check thenpm run buildwww/buildfolder for a file namedmy-component.js(or similar). The presence of this file confirms that the compiler split the component into its own chunk. - Serve the app locally and trigger the component. Start a dev server:
Open the application in the browser, open DevTools, and navigate to the Network tab. Interact with the page to causenpm startmy-componentto render (e.g., hover or click a button that reveals it). Verify that a separate request formy-component.jsappears and that it loads after the initial bundle. - Measure bundle size. Run:
The report will list the size of each chunk. Compare the total size of the initial bundle against a build without thenpm run build -- --reportlazyattribute to quantify the savings. - SSR placeholder check (if applicable). Render the page on the server (e.g.,
npm run prerenderor your SSR framework). Inspect the raw HTML to ensure a placeholder element (the fallback) is present before hydration. This prevents a flash of empty content.
Expected Checks & Validation
- Chunk presence.
my-component.jsexists inwww/buildand is not bundled intobundle.js. - Network fetch. The chunk is requested only after the user triggers the component. No 404 or import errors should appear in the console.
- Initial bundle size. The report shows a measurable reduction (e.g., 30–50 KB) compared to a non‑lazy build.
- Placeholder display. In SSR, the rendered HTML contains a
<my-component>tag with the fallback content. On the client, the fallback disappears once the chunk is loaded.
Recovery Options
If lazy‑loading causes a noticeable flicker or the component is critical to the first‑view UI, consider:
- Remove the
lazyattribute. The component will be bundled normally. - Preload the chunk. Add a
<link rel="preload" as="script" href="/build/my-component.js">tag to theindex.htmlso the browser starts fetching it early. - Polyfill for older browsers. Include a polyfill for
import()or use a bundler that generates a fallback script. - Graceful degradation. Wrap the import in a try/catch and display a static message if the module fails to load.
Limitations & Practical Tips
- Lazy components are not pre‑loaded; if the user never interacts with the component, the chunk may never be fetched.
- Older browsers (pre‑ES Modules) require a polyfill; test on target browsers before deployment.
- Ensure that the fallback UI is styled to match the final component to avoid layout shifts.
- When using dynamic import inside
componentWillLoad, remember that the import is executed on every instance; consider caching if the component is reused many times.
Conclusion
By marking a StencilJS component with lazy or using a dynamic import(), you can offload non‑essential code to a separate chunk, lowering the initial bundle size and improving perceived performance. The steps above provide a repeatable workflow: mark, fallback, build, verify, and recover. Use the network and bundle reports to confirm the savings, and keep an eye on placeholder rendering in SSR scenarios. With these practices, you’ll deliver faster first‑loads without sacrificing user experience.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.