Basic Example
Simple image that loads immediately (no conditions).
💡 Performance Note: While this pattern
loads the image immediately (like a standard
img), it still provides a benefit:
if JavaScript fails to load or execute, the image won't
load at all. This can be desirable for non-critical, optional images
that enhance but aren't essential to the content (e.g.,
decorative images, supplementary graphics, or marketing
banners).
⚠️ Important: Only use this pattern for
non-critical images that aren't referenced in your content.
Critical images that are part of your content should use
standard img tags to ensure they load even when
JavaScript is unavailable.
Basic Usage
<lazy-img
src="https://picsum.photos/800/400"
alt="Placeholder image">
</lazy-img>
Container Query Example (Default)
💡 Key Feature: Images only load when the container reaches the minimum size. If the container never gets that big, the image never loads — saving bandwidth! The default query type is "container".
Try it: Each container below starts smaller than needed. Drag the resize handle (bottom-right corner) to widen the container and watch the image lazy load when it hits the threshold.
Small Container (min-inline-size: 300px)
👉 Widen this container to 300px to see the image load. It starts at 200px (too small), so the image won't load until you resize it.
<lazy-img
src="https://picsum.photos/600/300?random=1"
alt="Landscape photo"
min-inline-size="300">
</lazy-img>
Medium Container (min-inline-size: 500px)
👉 Widen this container to 500px to trigger the image load.
<lazy-img
src="https://picsum.photos/800/400?random=2"
alt="Nature photo"
min-inline-size="500">
</lazy-img>
Media Query Example
Image loads based on viewport width. Uses
query="media" to check window width instead of
container width. This is perfect for saving bandwidth on
mobile devices — if the viewport is below 768px, this image
never loads.
Try it: Resize your browser window to see the effect. The image loads when the viewport reaches 768px wide.
Viewport Width Check (min-inline-size: 768px)
<lazy-img
src="https://picsum.photos/1000/500?random=3"
alt="Wide landscape photo"
min-inline-size="768"
query="media">
</lazy-img>
Current viewport width: px
View Mode (Scroll-Based Loading)
Image loads when scrolled into view using
IntersectionObserver. Uses query="view" for
scroll-based lazy loading. This is the classic "lazy load as
you scroll" pattern — images only load when they become
visible or are about to become visible.
Try it: Scroll down to see images load as
they enter the viewport. The timing can be controlled with
view-range-start.
Basic View Mode (default: entry 0%)
Loads as soon as any part of the image enters the viewport.
<lazy-img
src="https://picsum.photos/800/400?random=8"
alt="Scroll-based photo"
query="view">
</lazy-img>
👇 Scroll down to see the image below load
Image not loaded yet
Threshold-Based (entry 50%)
Loads when 50% of the image is visible.
<lazy-img
src="https://picsum.photos/800/400?random=9"
alt="Half-visible trigger"
query="view"
view-range-start="entry 50%">
</lazy-img>
👇 Scroll until 50% of the image is visible
Image not loaded yet
Preload Before Visible (entry -200px)
Starts loading 200px before the image enters the viewport (smooth UX).
<lazy-img
src="https://picsum.photos/800/400?random=10"
alt="Preloaded photo"
query="view"
view-range-start="entry -200px">
</lazy-img>
👇 Image loads 200px before you see it
Image not loaded yet
Responsive Images with srcset and sizes
Combine lazy loading with responsive images using srcset and sizes attributes.
srcset with sizes
👉 Widen this container to 400px to load the responsive image. Once loaded, the browser chooses the appropriate size from srcset.
<lazy-img
src="https://picsum.photos/800/400?random=4"
srcset="https://picsum.photos/400/200?random=4 400w,
https://picsum.photos/800/400?random=4 800w,
https://picsum.photos/1200/600?random=4 1200w"
sizes="(max-width: 600px) 400px,
(max-width: 1000px) 800px,
1200px"
alt="Responsive photo"
min-inline-size="400">
</lazy-img>
Named Breakpoints (CSS Custom Property)
Use the --lazy-img-mq CSS custom property on
:root to define named breakpoints. Set
different values at different media queries, and images will
load when the breakpoint name matches.
Example with Named Breakpoints
This demo sets --lazy-img-mq via CSS media
queries. Try resizing your browser window to see
different breakpoint values.
<style>
:root {
--lazy-img-mq: small;
}
@media (min-width: 768px) {
:root {
--lazy-img-mq: medium;
}
}
@media (min-width: 1024px) {
:root {
--lazy-img-mq: large;
}
}
</style>
<lazy-img
src="https://picsum.photos/1000/500?random=5"
alt="Scenic photo"
named-breakpoints="medium, large"
query="media">
</lazy-img>
Current breakpoint: small
Image loads when breakpoint is "medium" or "large" (viewport ≥ 768px)
Events
The component dispatches a
lazy-img:loaded event when the image loads.
Event Example
👉 Widen this container to 350px to trigger the load event.
<lazy-img
id="event-demo"
src="https://picsum.photos/600/300?random=6"
alt="Event demo photo"
min-inline-size="350">
</lazy-img>
<script>
document.getElementById('event-demo')
.addEventListener('lazy-img:loaded', (e) => {
console.log('Image loaded:', e.detail.src);
});
</script>
Event status: waiting for load...
State Attributes
The component provides two boolean attributes to track
state:
loaded (has the image been loaded?) and
qualifies (does it currently meet conditions to
show?). These allow you to control visibility with CSS.
Controlling Visibility
This example hides images that loaded but no longer qualify (e.g., after device rotation or resize).
<style>
/* Hide loaded images that don't qualify */
lazy-img[loaded]:not([qualifies]) {
display: none;
}
/* Show placeholder when qualifies but not loaded */
lazy-img[qualifies]:not([loaded])::before {
content: '⏳ Waiting to load...';
display: block;
padding: 2em;
background: #f0f0f0;
}
</style>
<lazy-img
src="https://picsum.photos/600/300"
alt="State demo"
min-inline-size="400">
</lazy-img>
API Reference
For complete documentation of attributes, events, and browser support, see the README.