Lazy‑Loading Pages in Framework 7 with Async Route Components
Learn how to lazy‑load Framework 7 pages by declaring async route components that return dynamic import promises, with a concrete Webpack example, verification steps, and common pitfalls.
16 Apr 2026, 16:55 UTC

Why lazy‑load routes?
Framework 7 single‑page applications often ship a single JavaScript bundle that contains every page component. As the app grows, the initial download becomes a performance bottleneck—especially on mobile networks. The framework solves this by letting you declare a route’s component property as an async function that returns a Promise. When the router navigates to that route it awaits the promise, injects the resolved markup, and transitions without a full page reload.
How the mechanism works
When you use a bundler (Webpack, Vite, Rollup, etc.) the dynamic import() expression creates a separate code‑split chunk. The router does three things automatically:
- Calls the async function you supplied.
- Waits for the returned promise to resolve.
- Inserts the resolved template (HTML, Template7, or JSX) into the view and runs the normal page‑init lifecycle.
Because the chunk is fetched only on first navigation, the initial bundle stays small and subsequent visits reuse the already‑loaded module (the browser caches the chunk).
Worked configuration example
The following steps assume a Framework 7 v8 project scaffolded with the Webpack template (the same pattern works with Vite).
1. Create the project (run in a terminal with write access to the target folder)
npx framework7 create my-app --template=webpack
cd my-app
npm install
Permissions: you need write access to the project directory and network access to download npm packages.
2. Add a lazy page
Create src/pages/lazy.html with minimal markup:
<template>
<div class="page" data-name="lazy">
<div class="navbar">
<div class="navbar-bg"></div>
<div class="navbar-inner">
<div class="title">Lazy Page</div>
</div>
</div>
<div class="page-content">
<p>This page was loaded on demand.</p>
</div>
</div>
</template>
3. Register the route with an async component
Edit src/routes.js (or the routes section of app.js) and add:
export default [
{
path: '/lazy/',
// async component – returns a Promise that resolves to the page module
component: () => import('./pages/lazy.html'),
},
// …other routes
];
The import() call is a dynamic import; Webpack treats it as a split point and emits a separate chunk (e.g., lazy.abc123.js).
4. Run the dev server and verify
npm run dev
Open the app (usually http://localhost:8080), open DevTools → Network tab, filter by JS, then click a link that navigates to /lazy/. You should see:
- A new chunk request (e.g.,
lazy.abc123.js) appear before the page renders. - No full‑page reload—the router transitions smoothly.
- On a second visit to
/lazy/the chunk is served from memory/cache (no second network request).
Limits and common mistakes
| Issue | Why it matters | Fix / Work‑around |
|---|---|---|
| Returning a plain string instead of a Promise | The router treats the value as a synchronous component, causing a flash of unstyled content or navigation errors. | Always use () => import(...) or an async function() { return await import(...); }. |
Running from the file system (file://) |
Browsers block dynamic imports for file:// origins, so the chunk never loads. |
Serve the app through a local HTTP server (the dev server does this) or build for production and host on a web server. |
| Incompatible component format | Framework 7 expects the resolved module to export a template string, a Template7 compiled function, or a JSX component. | Keep page files as .html (Template7) or .jsx with the appropriate loader configured. |
| Missing bundler configuration for code splitting | Some minimal Webpack setups disable optimization.splitChunks. |
Ensure optimization: { splitChunks: { chunks: 'all' } } is present (default in the Framework 7 Webpack template). |
Verification checklist
- Chunk creation: After
npm run build, inspect thewww/js(ordist) folder – a separatelazy.[hash].jsfile should exist. - Network trace: In the running dev server, navigate to the lazy route and confirm a single chunk request appears.
- Cache reuse: Navigate away and back; the Network tab should show 0 bytes transferred for the chunk.
- Functional test: Verify that page‑init callbacks (e.g.,
on: { pageInit }) fire correctly on the lazy page.
When to consider alternatives
If your app targets environments without a bundler (e.g., a plain Cordova/Capacitor project that loads file://), lazy‑loading via dynamic import will not work. In those cases you can:
- Pre‑load all pages at startup (accept the larger bundle).
- Use Framework 7’s
router.preload()API to fetch HTML fragments via XHR/fetch and register them manually.
Bottom line
Declaring a route’s component as an async function that returns a dynamic import() is the idiomatic way to code‑split a Framework 7 application. It keeps the initial payload small, leverages the bundler’s chunk caching, and requires only a one‑line change per lazy route. Just remember to serve through a bundler‑aware HTTP server and ensure the returned value is a genuine Promise.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.