Lazy-Loading StencilJS Components for Smaller Production Bundles
Learn how to use the lazy flag in StencilJS to split components into separate chunks, reduce initial JavaScript payload, and verify dynamic imports in a production build.
25 Dec 2025, 18:54 UTC

The problem: one bundle ships every component
By default, a Stencil production build compiles every component in the project into the main JavaScript bundle. As an app grows, that synchronous payload delays parsing and execution on slow connections. The useful takeaway: setting lazy: true on the @Component decorator tells the Stencil compiler to emit a separate chunk for that component and replace its synchronous definition with a dynamic import() call, so the browser downloads the component code only when it is actually used.
Desired outcome
After following this guide, your production build will contain a main bundle that references the lazy component through a dynamic import, plus a separate hashed chunk file for that component. The initial payload will be smaller, and the component will load over the network after the main bundle has executed.
Prerequisites
- An existing Stencil project with the standard compiler pipeline installed.
- Node.js and npm available in your terminal.
- A target browser that supports native ES modules, which is typical for modern browsers.
- Basic familiarity with the project’s
src/componentsdirectory and thewwwoutput folder.
This guide assumes a Stencil version that supports the lazy property on @Component. The flag has been part of the compiler for several major releases; if you maintain a very old project, confirm the property exists in your installed version before relying on it.
Focused procedure
1. Mark the component as lazy
Open or create the component file you want to defer. Add lazy: true to the @Component decorator options.
// src/components/lazy-item.tsx
import { Component, h } from '@stencil/core';
@Component({
tag: 'lazy-item',
lazy: true
})
export class LazyItem {
render() {
return <div>Lazy loaded content</div>;
}
}
When lazy is omitted or set to false, Stencil bundles the component synchronously into the main chunk. With lazy: true, the compiler instead emits a dynamic import wrapper. The generated code uses the Stencil lazy-loader utility, which resolves the chunk and instantiates the component once it arrives.
2. Move initialization out of module scope
A lazy component must not rely on synchronous side effects that run while the main bundle executes. If your component currently performs work at the top level of its module—such as registering a global listener or reading window immediately—move that logic into the componentWillLoad or connectedCallback lifecycle hooks. Those hooks run after the chunk has been fetched and the component is being connected.
3. Reference the tag and build
Add the custom element to the page or parent component where you want it to appear:
<lazy-item></lazy-item>
From the project root, run the production build. Standard user permissions are sufficient.
npm run build
Expected check: the command exits successfully and the www/build directory is updated. If the build fails, the compiler will report syntax or configuration errors before any output is emitted.
4. Inspect the generated output
Open the www/build folder. You should see a separate chunk file for the lazy component with a content-based hash in its name, for example lazy-item.[hash].js. The main bundle should no longer contain the full component class; instead, it should contain an import() expression pointing to that chunk.
Expected checks
To verify the behavior in a browser, serve or open www/index.html and open DevTools → Network. Reload the page and confirm that a separate request for the lazy chunk fires after the main bundle has loaded. In the Sources or Debugger panel, search the main bundle source for import( and confirm it references the lazy chunk filename.
A quick diagnostic decision: if the main bundle still contains the component class inline, the lazy flag was likely not picked up—re-check the decorator syntax and rebuild. If the chunk exists but never loads, verify that the <lazy-item> tag is actually present in the rendered DOM and that no JavaScript error blocked the dynamic import.
Recovery options and limitations
- Chunk not generated: Confirm the decorator uses
lazy: trueexactly, then delete thewwwfolder and rebuild to rule out stale output. - Component does not render: Check the browser console for failed network requests or module resolution errors. Ensure no top-level side effects remain in the component file.
- Extra latency: Lazy loading reduces initial payload but adds one network round trip for the component chunk. Avoid using it for above-the-fold elements that block first paint.
- SSR hydration mismatches: When server-side rendering is involved, the server must be able to render the lazy component’s fallback, or lazy should be disabled for the SSR pass. Otherwise the client may hydrate a different tree than the server produced.
Because Stencil applies content-based hashing to build assets, the lazy chunk filename changes when its content changes. That enables long-term caching: browsers can reuse the chunk indefinitely until the component code itself is modified.
Practical verification
The most direct check is visual and network-based. After building, open the app in a browser with DevTools open. Reload and watch the Network panel. You should see the main bundle load first, followed by a separate request for the hashed lazy chunk. If both appear and the component renders, lazy loading is working as intended.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.