SmartImage
A declarative, attribute-driven image Web Component with lazy loading, shimmer and spinner skeletons, fade-in animation, fallback on error, rounded/circle shapes, hover zoom, click-to-preview modal, aspect ratio, and captions — all from HTML attributes.
<script src="smart-image.js"></script>
Live Playground
Toggle attributes on the left to see the real <smart-image> component update live.
Controls
<smart-image src="https://picsum.photos/id/10/560/360" width="280" height="180" caption="Mountain lake at dusk" animation-type="shimmer" fit="cover" rounded ></smart-image>
Lazy Loading
Images are lazy-loaded by default via IntersectionObserver — the network request only fires when the image enters the viewport. Set lazy="false" to force an eager load.
<!-- Default: lazy load via IntersectionObserver --> <smart-image src="/media/photo.jpg" width="300" height="200" ></smart-image> <!-- Eager: loads immediately regardless of viewport --> <smart-image src="/media/hero.jpg" lazy="false" width="1200" height="600" ></smart-image>
Skeleton Types
While the image loads, a placeholder skeleton is shown. shimmer (default when width & height are set) animates a gradient sweep. spinner shows a rotating ring — useful for circular avatars or when dimensions aren't fixed.
<smart-image src="/media/photo.jpg" width="300" height="200" animation-type="shimmer" ></smart-image> <smart-image src="/media/avatar.jpg" width="80" height="80" animation-type="spinner" circle ></smart-image>
Rounded & Circle
Use rounded for a card-style corner radius, or circle for a perfect circular crop — ideal for avatars.
<smart-image src="..." width="220" height="160"></smart-image> <smart-image src="..." width="220" height="160" rounded></smart-image> <smart-image src="..." width="80" height="80" circle></smart-image>
Hover Zoom
Add hover-zoom to scale the image to 107% on hover with a smooth cubic-bezier transition. The image is clipped by its wrapper so no layout shift occurs.
<smart-image src="/media/photo.jpg" width="300" height="200" rounded hover-zoom ></smart-image>
Click Preview
Add click-preview to open a fullscreen lightbox modal when the image is clicked. Press Esc or click outside to close.
<smart-image src="/media/photo.jpg" width="300" height="200" rounded hover-zoom click-preview caption="Click to open preview" ></smart-image>
Aspect Ratio
Use aspect-ratio instead of a fixed height for fluid, responsive images. Combine with fit to control how the image fills the box (cover, contain, fill).
<!-- Fluid 16:9 banner — scales with its container --> <smart-image src="/media/banner.jpg" aspect-ratio="16/9" style="width:100%" fit="cover" rounded ></smart-image> <!-- Square thumbnail --> <smart-image src="/media/thumb.jpg" aspect-ratio="1/1" style="width:120px" fit="cover" rounded ></smart-image>
Fallback & Error
Set fallback-src to automatically try a second image if the primary URL fails. If the fallback also fails, a broken-image placeholder with a Retry button is shown. An image-error event fires either way.
<!-- With fallback — tries a second URL on error --> <smart-image src="/media/user-upload.jpg" fallback-src="/media/placeholder.jpg" width="200" height="200" rounded ></smart-image> <!-- Without fallback — shows retry UI on error --> <smart-image src="/media/might-fail.jpg" width="200" height="200" rounded ></smart-image>
Caption
Add a caption attribute to render a small text label beneath the image wrapper. Use alt for screen reader accessibility — it is separate from the visible caption.
<smart-image src="/media/meadow.jpg" width="300" height="200" rounded caption="Alpine meadow, July 2024" alt="Photo of an alpine meadow" ></smart-image>
Attributes
| Attribute | Type | Description | Default |
|---|---|---|---|
| src | string | Required. The image URL to load. | — |
| fallback-src | string | A secondary URL to attempt if src fails. Only tried once. |
— |
| alt | string | Alternative text for the <img> element — used by screen readers. |
"" |
| caption | string | Visible text rendered below the image wrapper as a small <p>. |
— |
| width | number | string | Sets the wrapper width. Numbers are converted to px; strings are used as-is (e.g. "100%"). |
— |
| height | number | string | Sets the wrapper height. Same rules as width. |
— |
| aspect-ratio | string | CSS aspect-ratio value (e.g. "16/9", "1/1"). Use instead of a fixed height for fluid images. |
— |
| fit | string | CSS object-fit value for the image inside its wrapper. Options: cover, contain, fill. |
cover |
| animation-type | string | Skeleton style while loading. shimmer shows a gradient sweep; spinner shows a rotating ring. Auto-selected based on whether width & height are set. |
shimmer |
| rounded | boolean | Adds a 10px border-radius to the wrapper for a card-style appearance. | false |
| circle | boolean | Clips the image into a perfect circle. Best used with equal width and height. | false |
| hover-zoom | boolean | Scales the image to 107% on hover with a smooth transition. Clipped to the wrapper bounds. | false |
| click-preview | boolean | Opens a fullscreen lightbox modal on click. Close with Esc or click outside. | false |
| lazy | boolean | Set to "false" to load the image immediately regardless of viewport position. |
true |
Events
| Event | detail | Fires when |
|---|---|---|
| image-loaded | { src } | The image has loaded and the fade-in animation has started. |
| image-error | { src } | Both the primary src and fallback-src (if set) have failed to load. |
const img = document.querySelector('smart-image'); img.addEventListener('image-loaded', (e) => { console.log('Image loaded:', e.detail.src); }); img.addEventListener('image-error', (e) => { console.warn('Image failed:', e.detail.src); // log to error tracking, swap in a different URL, etc. });