Lazy Loading & Code Splitting
Lazy loading moves non-essential work out of the first page load. The goal is not to hide slow code; it is to avoid downloading, parsing, and executing code or media before the user can benefit from it. Good candidates are routes, modals, drawers, charts, editors, maps, and below-the-fold media.
Dynamic Imports
Bundlers split dynamic imports into separate chunks. The initial bundle gets smaller, and the chunk loads only when the route or feature is needed.
type RouteModule = {
render: () => void;
};
export async function loadSettingsRoute(): Promise<RouteModule> {
const module = await import("./routes/settings");
return module;
}
React wraps this with React.lazy. In Svelte islands, you usually keep the island focused and dynamically import heavy helpers or data visualizations inside the component when a user action requires them.
Native Media Lazy Loading
Images and iframes support browser-native lazy loading. In TypeScript-heavy codebases, you can still centralize attributes through a typed helper.
type LazyImageAttrs = {
src: string;
alt: string;
loading: "lazy";
decoding: "async";
};
export function lazyImage(src: string, alt: string): LazyImageAttrs {
return { src, alt, loading: "lazy", decoding: "async" };
}
Do not lazy-load the largest above-the-fold image because it usually affects Largest Contentful Paint.
IntersectionObserver Pattern
For custom components, place a sentinel near the content and mount the expensive component after it intersects.
export function observeOnce(
element: Element,
onVisible: () => void,
rootMargin = "200px",
) {
const observer = new IntersectionObserver(([entry]) => {
if (!entry?.isIntersecting) return;
onVisible();
observer.disconnect();
}, { rootMargin });
observer.observe(element);
return () => observer.disconnect();
}
Prefetch hints are useful when intent is likely. For example, prefetch a settings route after the user opens the account menu, not on every page load.
TIP
Lazy-load components that are off-screen or behind a user action (modal, drawer, tab). Don’t lazy-load things the user sees immediately — that adds a waterfall.
Further Learning
Search these terms to go deeper:
- “dynamic import code splitting Vite” — how chunks are created by modern bundlers
- “IntersectionObserver lazy loading component” — viewport-triggered rendering patterns
- “native image loading lazy LCP” — when lazy loading helps or harms metrics
- “link rel prefetch route chunks” — warming likely future navigation