Lazy loading is a simple idea applied to a lot of different things: do not load something until it is actually needed. An image below the fold, a route the user has not navigated to yet, a rich text editor most visitors never open, a chat widget script — all of it can wait. The pattern looks different for each one, from a single HTML attribute to a dynamic import wrapped in a loading state, but the decision is the same: defer the cost until the moment it is worth paying.
What changed in 2026
- Suspense-based lazy loading became the default pattern for components. Most meta-frameworks now build component-level lazy loading on top of React Suspense or an equivalent, rather than a custom loading-state pattern per project.
- Native loading="lazy" is the default recommendation for images and iframes. JavaScript-based intersection observer approaches are now reserved for cases the native attribute cannot handle.
- Dedicated third-party script loading strategies matured. Delaying analytics, chat widgets, and ad scripts until after the main content loads is now a standard, named strategy rather than a manual workaround.
- Performance scoring keeps pushing teams to lazy-load aggressively below the fold, while treating the largest visible element as an exception that must never be deferred.
The general pattern, not just images
Lazy loading shows up in a few recognizable forms: native loading="lazy" for images and iframes, a router that loads each route as its own chunk, a component wrapped in a lazy boundary for a heavy or rarely used feature, and a delayed-load strategy for third-party scripts that are not essential to the first render.
import { lazy, Suspense } from "react";
const RichTextEditor = lazy(() => import("./RichTextEditor.js"));
function PostForm({ showEditor }) {
return (
<div>
{showEditor && (
<Suspense fallback={<p>Loading editor...</p>}>
<RichTextEditor />
</Suspense>
)}
</div>
);
}
The editor bundle, often one of the heaviest single dependencies on a page, is not downloaded at all until showEditor becomes true. For images specifically, the same idea needs only one attribute and no JavaScript at all: <img src="photo.jpg" loading="lazy" alt="description">.
Lazy loading versus code splitting
These two get used interchangeably, but they are not the same thing. Code splitting is the bundler mechanism that creates a separate chunk in the first place. Lazy loading is the runtime decision about when to actually request that chunk, or an image, or a script. JavaScript lazy loading needs code splitting to exist; the term "lazy loading" also covers images and other resources that have nothing to do with bundles at all.
What to lazy load and how
| What you are deferring |
How to lazy load it |
Native or needs a library |
| Below-the-fold images |
loading="lazy" attribute |
Native |
| Below-the-fold iframes |
loading="lazy" attribute |
Native |
| A route in a single-page app |
Dynamic import via the router |
Usually built into the framework |
| A heavy, rarely used component |
lazy() plus a loading boundary |
Needs a small amount of framework code |
| A third-party analytics or chat script |
Defer or a delayed-load strategy |
Usually a small custom loader |
When not to lazy load
Never lazy-load the largest contentful paint element — a hero image, an above-the-fold heading, anything the user sees the instant the page renders. Do not lazy-load anything the user needs to interact with immediately. And skip lazy-loading pieces so small that the overhead of an extra request costs more than the bytes it deferred.
Common mistakes
Lazy-loading the hero image. This is the single most common performance regression tied to lazy loading — it directly delays the metric that measures perceived speed.
No loading fallback for a lazy component. A component that pops in with no placeholder causes a layout jump that reads as broken, even though the loading worked correctly.
Lazy-loading something needed on most visits. If most users trigger the deferred piece anyway, deferring it adds a delay for the common case to optimize an uncommon one.
Mixing up the terminology on a team. Lazy loading, code splitting, and tree shaking get used loosely as synonyms, which leads to real miscommunication about what a change actually does.
FAQ
Is lazy loading the same as code splitting?
No. Code splitting is the bundler mechanism that creates a separate chunk. Lazy loading is the runtime decision of when to load that chunk, an image, or a script. Lazy loading needs code splitting for JavaScript, but the term also covers resources that have nothing to do with bundles.
Should I lazy load my hero image?
No. If it is the largest visible element on first paint, lazy loading it delays the metric that matters most for perceived speed. Load it eagerly instead.
How do I lazy load a component only when it scrolls into view?
Combine a lazy-loaded component with an intersection observer that flips a piece of state to true once the container enters the viewport, then render the component only after that state changes.
Where to go next