Lazy‑Loading StencilJS Components with `lazy: true`
Learn how StencilJS's `lazy: true` flag creates separate chunks for web components, reducing initial bundle size and deferring load until the component is actually needed.
22 Oct 2025, 02:21 UTC

Problem: Large initial bundle hurts first paint
When a StencilJS application ships all components in a single JavaScript bundle, the browser must download, parse, and execute that code before it can render anything useful. For pages that only need a few components on the initial view, the extra weight delays First Contentful Paint (FCP) and increases time‑to‑interactive.
Thesis
Adding lazy: true to a component’s @Component decorator tells Stencil to generate a separate ES‑module chunk that is fetched only when the component is first inserted into the DOM. This code‑splitting technique reduces the initial bundle size and defers the cost of loading heavyweight components until they are actually needed.
How lazy: true works in Stencil
During the build, Stencil’s compiler wraps the lazy component’s class in a dynamic import (import(/* … */ './photo-gallery.js')) and emits a separate chunk for that import. The generated hydrate script registers the component tag with a factory that returns a promise; when the browser encounters the tag, it triggers the import, waits for the chunk to load, then instantiates and renders the component. No extra Rollup configuration is required—Stencil handles the split automatically.
Worked example: a lazy <photo-gallery>
Create a new Stencil project (run in a terminal with normal user permissions):
npm init stencil@latest my-lazy-app cd my-lazy-app npm installRisk: ensure you have Node ≥ 14 and internet access for the bootstrap.
Add a lazy component
src/components/photo-gallery/photo-gallery.tsx:import { Component, h } from '@stencil/core'; @Component({ tag: 'photo-gallery', styleUrl: 'photo-gallery.css', lazy: true // ← enables code‑splitting }) export class PhotoGallery { render() { return (My Photo Gallery
{/* …gallery markup… */} ); } }Use the component in a page, e.g.
src/components/app-home/app-home.tsx:import { Component, h } from '@stencil/core'; @Component({ tag: 'app-home', styleUrl: 'app-home.css' }) export class AppHome { state showGallery = false; toggleGallery() { this.showGallery = true; } render() { return ( Welcome this.toggleGallery()}>Load Gallery {this.showGallery && } ); } }Build the app:
npm run buildInspect the
www/buildfolder. You should see at least two files: a main bundle (e.g.,app-home.js) and a separate chunk for the lazy component (e.g.,photo-gallery.js). The main bundle does not contain the gallery’s class code.Serve the build locally and verify lazy loading:
npx serve wwwOpen Chrome DevTools → Network tab, disable cache, reload the page. Initially you will see only the main bundle and the HTML. Clicking “Load Gallery” triggers a new request for
photo-gallery.js. After the request finishes, the gallery appears in the DOM.Practical check: In the Network tab, filter by JS and confirm that
photo-gallery.jshas aStatus Code: 200 (from ServiceWorker)or200 (from disk cache)only after the button click.
Trade‑offs and limitations
First‑use latency: The browser must wait for the network request (typically a few‑tens of milliseconds on a fast connection) before the component renders. This is usually acceptable for non‑critical UI but can be noticeable on slow networks.
SSR complexity: When rendering on the server, Stencil falls back to a hydrate‑based approach; the lazy component’s HTML is not included in the initial markup unless you manually pre‑render it or use a server‑side loader. Ensure your SSR setup can handle the promise‑based import or provide a fallback.
Request overhead: Excessive lazy loading can create many small chunks, increasing round‑trip overhead. Profile with Chrome’s “Request blocking” or WebPageTest to verify that the reduction in bytes outweighs the extra requests.
Actionable guidance
Apply lazy: true to components that are:
- Large (e.g., data‑heavy charts, editors, galleries).
- Not required for the initial view (behind a tab, modal, or user‑triggered action).
- Used infrequently across page views.
After adding the flag, run a production build, inspect the generated chunks, and validate lazy loading in the Network tab as shown above. If you notice a noticeable delay, consider:
- Pre‑fetching the chunk with
link rel="prefetch"when you anticipate the user’s action. - Grouping related lazy components into a shared chunk via Stencil’s
collectionfeature if many small lazy components cause request overhead.
By measuring bundle size before and after, and confirming the network behavior, you can confidently decide whether lazy loading improves the perceived performance of your StencilJS application.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.