A drop-in replacement for the native <img> element with lazy loading, responsive images, blur placeholders, skeleton loaders, AVIF/WebP detection, click-to-zoom, and automatic retry built in — delivering better performance, UX, and DX with zero runtime dependencies.
Lazy Loading • Responsive Images • Blur Placeholder • Skeleton Loader • Image Zoom • Retry Logic • AVIF/WebP • Core Web Vitals • TypeScript
🌐 Live Demo: https://react-smart-image.netlify.app
Modern React applications often require multiple libraries just to build a complete image experience.
One library for lazy loading.
Another for blur placeholders.
Another for responsive images.
Another for zoom.
Another for retry logic.
Managing all of these increases dependencies, bundle size, and maintenance.
React Smart Image solves this by combining everything into one lightweight, production-ready component.
Simply install one package and start building.
npm install @concatstring/react-smart-imageor
yarn add @concatstring/react-smart-imageor
pnpm add @concatstring/react-smart-imagereact >= 18
react-dom >= 18Using React Smart Image is as simple as replacing your existing <img>.
import { SmartImage } from "@concatstring/react-smart-image";
export default function App() {
return (
<SmartImage
src="/photo.jpg"
alt="Beautiful landscape"
width={800}
height={600}
/>
);
}That's it.
No configuration.
No provider required.
Works exactly like a normal image.
Enable multiple powerful features with simple props.
<SmartImage
src="/product.jpg"
alt="Product"
lazy
responsive
skeleton
placeholder="blur"
format="auto"
zoom
retry={2}
fallback="/images/placeholder.png"
/>Instead of combining multiple image libraries, React Smart Image provides everything in one component.
- ✅ Lazy Loading
- ✅ Priority Loading
- ✅ Responsive Images
- ✅ Aspect Ratio
- ✅ Image Preloading
- ✅ AVIF & WebP Detection
- ✅ Progressive Thumbnail Loading
- ✅ Blur Placeholder
- ✅ Skeleton Loader
- ✅ Smooth Image Transitions
- ✅ Click to Zoom
- ✅ Magnifier Lens
- ✅ Fullscreen Viewer
- ✅ Automatic Retry
- ✅ Exponential Backoff
- ✅ Error Fallback Images
- ✅ Image Cache Utilities
- ✅ Drop-in
<img>Replacement - ✅ Zero Runtime Dependencies
- ✅ TypeScript First
- ✅ SmartImageProvider
- ✅ Image Presets
- ✅ Responsive URL Builder
- ✅ Analytics Callbacks
- ✅ SSR Friendly
| Feature | HTML <img> |
Next.js Image | React Smart Image |
|---|---|---|---|
| Lazy Loading | ✅ | ✅ | ✅ |
| Responsive Images | ❌ | ✅ | ✅ |
| Blur Placeholder | ❌ | ✅ | ✅ |
| Skeleton Loader | ❌ | ❌ | ✅ |
| Retry Failed Images | ❌ | ❌ | ✅ |
| Error Fallback | ❌ | ❌ | ✅ |
| Zoom Viewer | ❌ | ❌ | ✅ |
| Magnifier Lens | ❌ | ❌ | ✅ |
| Image Presets | ❌ | ❌ | ✅ |
| Global Provider | ❌ | ❌ | ✅ |
| AVIF/WebP Auto Detection | ❌ | ❌ | ✅ |
| Progressive Thumbnail | ❌ | ❌ | ✅ |
| TypeScript | ❌ | ✅ | ✅ |
| Framework Independent | ✅ | ❌ | ✅ |
| Your Goal | Feature |
|---|---|
| Improve LCP | priority |
| Improve CLS | aspectRatio |
| Faster Loading | responsive |
| Better UX | placeholder="blur" |
| Loading Animation | skeleton |
| Product Gallery | zoom |
| Magnifying Glass | magnifier |
| Broken Image | fallback |
| Retry Failed Images | retry |
| Modern Formats | format="auto" |
- Installation
- Quick Start
- Lazy Loading
- Priority Loading
- Aspect Ratio
- Skeleton Loader
- Blur Placeholder
- Image Transition
- Responsive Images
- AVIF & WebP
- Thumbnail Loading
- Zoom
- Retry & Fallback
- SmartImageProvider
- Image Presets
- Performance Tips
- TypeScript
- API Reference
- FAQ
- License
Load images only when they're about to enter the viewport, reducing initial page load time and bandwidth usage.
Perfect for:
- Blog posts
- Product listings
- Galleries
- Long pages
<SmartImage
src="/gallery.jpg"
alt="Gallery Image"
width={800}
height={500}
lazy
/>- Uses Intersection Observer
- Starts loading 100px before entering the viewport
- Automatically disabled when
priorityis enabled
💡 Use
lazyfor every image that is below the fold.
Priority images are loaded immediately with the highest browser priority.
Ideal for:
- Hero banners
- Landing page images
- First visible product image
- Largest Contentful Paint (LCP)
<SmartImage
src="/hero.jpg"
alt="Hero"
width={1600}
height={700}
priority
/>When enabled, React Smart Image automatically:
- disables lazy loading
- sets
loading="eager" - sets
fetchpriority="high" - injects
<link rel="preload">
Combine it with responsive — the preload link mirrors the rendered image via imagesrcset/imagesizes, so the browser preloads the exact candidate it will display (one request, not two):
<SmartImage
src="/hero.jpg"
alt="Hero"
priority
responsive
sizes={{ mobile: 480, tablet: 768, desktop: 1200 }}
/><link
rel="preload"
as="image"
href="/hero.jpg"
imagesrcset="/hero.jpg?w=480 480w, /hero.jpg?w=768 768w, /hero.jpg?w=1200 1200w"
imagesizes="(max-width:640px)480px,(max-width:1024px)768px,1200px"
fetchpriority="high"
/>
⚠️ Only usepriorityfor one or two images per page.
Reserve image space before it loads to eliminate layout shifts and improve Core Web Vitals.
<SmartImage src="/banner.jpg" alt="Banner" aspectRatio={16 / 9} />Or:
<SmartImage src="/banner.jpg" alt="Banner" width={1200} aspectRatio="16 / 9" />Supported values:
aspectRatio={16 / 9}
aspectRatio={4 / 3}
aspectRatio={1}
aspectRatio="21 / 9"aspectRatio accepts a number | string, passed straight to the CSS aspect-ratio property. {16 / 9} isn't special syntax — it's plain JS division evaluated before the prop reaches CSS (16 / 9 → 1.777…), just a readable way to write "1.777" while keeping the 16:9 intent obvious.
With the number form
{16 / 9}JavaScript does the division; with the string form"16 / 9"the/stays literal inside the quotes. Both end up as valid CSS.
💡 Using
aspectRatiomeans you usually don't need to specifyheight.
Display an animated loading placeholder while the image downloads.
ℹ️ Requires
widthandheight(oraspectRatio) so the placeholder has the right dimensions.
<SmartImage src="/avatar.jpg" alt="Avatar" width={120} height={120} skeleton />Customize the appearance:
<SmartImage
src="/avatar.jpg"
alt="Avatar"
width={120}
height={120}
skeleton
skeletonColor="#1F2937"
skeletonHighlightColor="rgba(255,255,255,.12)"
/>Perfect for:
- User profiles
- Product cards
- Dashboards
- Social feeds
Display a tiny blurred preview until the full image loads.
ℹ️ Requires
widthandheight(oraspectRatio) so the placeholder has the right dimensions.
<SmartImage
src="/mountain.jpg"
alt="Mountain"
width={1200}
height={700}
placeholder="blur"
blurDataURL="data:image/jpeg;base64,..."
/>Benefits
- Better perceived performance
- Smooth image loading
- Professional appearance
💡
skeletonandplaceholder="blur"can be combined — both layers render, with the skeleton on top.
Don't want to generate Base64 placeholders? Enable automatic blur generation.
<SmartImage
src="/mountain.jpg"
alt="Mountain"
width={1200}
height={700}
placeholder="blur"
autoBlur
/>Customize preview size:
<SmartImage autoBlur blurWidth={32} />ℹ️ Requires an image server or CDN capable of resizing images.
Examples:
- Cloudinary
- ImageKit
- Imgix
- CloudFront
- Thumbor
By default, skeleton/placeholder="blur" reveal the final image with a simple opacity crossfade — a plain <SmartImage> with neither set gets no fade at all unless you add transition or fade. thumbnail mode is different: the image swaps src in place with no fade to animate, so transition/fade are ignored there.
<SmartImage src="/photo.jpg" alt="Photo" skeleton transition="scale" />Available animations:
- fade
- scale
- grow
- rotate
- flip
- slide-up
- slide-left
- reveal
- none
Control animation duration:
<SmartImage transition="fade" transitionDuration={500} />Simple shorthand:
<SmartImage src="/photo.jpg" fade />
⚠️ transitionis ignored whenthumbnailis set, since that mode swapssrcin place rather than revealing over a hidden state.
Show a lightweight thumbnail instantly while the high-resolution image loads in the background.
<SmartImage
src="/photo-large.jpg"
thumbnail="/photo-thumb.jpg"
width={1200}
height={800}
/>Perfect for:
- Photography websites
- Product galleries
- Portfolio pages
Automatically serve AVIF or WebP when available.
<SmartImage src="/banner.jpg" avif webp />Or simply:
<SmartImage src="/banner.jpg" format="auto" />Loading order:
banner.avif → banner.webp → banner.jpg
Supported source formats:
- JPG
- JPEG
- PNG
- GIF
- BMP
- TIFF
Automatically retry failed image requests.
<SmartImage src="/cdn-image.jpg" retry={3} retryDelay={500} />Retry timing:
Attempt 1 → 500ms
Attempt 2 → 1000ms
Attempt 3 → 2000ms
Uses exponential backoff to reduce unnecessary network traffic.
Display a replacement image when all retries fail.
<SmartImage src="/missing-image.jpg" fallback="/images/no-image.png" />Combine with retry:
<SmartImage
src="/cdn-image.jpg"
retry={2}
retryDelay={1000}
fallback="/images/no-image.png"
/>This provides the best user experience for unreliable networks.
React Smart Image is designed so features work together seamlessly.
<SmartImage
src="/product.jpg"
alt="Product"
width={600}
height={600}
lazy
responsive
placeholder="blur"
skeleton
transition="fade"
format="auto"
retry={2}
fallback="/images/placeholder.png"
/>One component. Everything built in. No additional image libraries required.
Serve the right image for every screen size to improve loading performance and reduce bandwidth usage.
React Smart Image supports two responsive strategies:
- srcSet (default)
- viewport
Let the browser automatically choose the best image based on:
- Screen size
- Device Pixel Ratio (DPR)
- Browser capabilities
<SmartImage
src="/banner.jpg"
alt="Banner"
responsive
sizes={{ mobile: 480, tablet: 768, desktop: 1400 }}
/>Generated HTML:
<img
srcset="/banner.jpg?w=480 480w, /banner.jpg?w=768 768w, /banner.jpg?w=1400 1400w"
sizes="(max-width:640px)480px,(max-width:1024px)768px,1400px"
/>- Marketing websites
- Blogs
- Landing pages
- Ecommerce
- Most applications
Always load the image matching the viewport width.
<SmartImage
src="/banner.jpg"
responsive
strategy="viewport"
sizes={{ mobile: 480, tablet: 768, desktop: 1400 }}
/>Unlike srcSet, viewport ignores DPR. A mobile device always receives the mobile image.
Breakpoints: mobile ≤ 640px, tablet ≤ 1024px, desktop above. Updates live on resize.
- Saving bandwidth
- Internal dashboards
- Admin panels
- Mobile-first applications
| Feature | srcSet | viewport |
|---|---|---|
| Browser chooses image | ✅ | ❌ |
| DPR Aware | ✅ | ❌ |
| Lowest bandwidth | ❌ | ✅ |
| Highest image quality | ✅ | ✅ |
| Recommended | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ |
Different CDNs generate resized images differently. By default React Smart Image generates:
/photo.jpg?w=480
If your CDN uses another format, provide a custom URL builder.
<SmartImage
src="/photo.jpg"
responsive
sizes={{ mobile: 480, tablet: 768, desktop: 1200 }}
srcSetBuilder={(src, width) => src.replace(/(\.\w+)$/, `-${width}$1`)}
/>Generated:
/photo-480.jpg
/photo-768.jpg
/photo-1200.jpg
⚠️ srcSetBuildershould be a pure function — it's called once per breakpoint width (and again atblurWidthwhenautoBluris on), and may re-run on every resize.
Cloudinary
srcSetBuilder={(src, width) => `https://res.cloudinary.com/demo/image/upload/w_${width}${src}`}ImageKit
srcSetBuilder={(src, width) => `${src}?tr=w-${width}`}Imgix
srcSetBuilder={(src, width) => `${src}?w=${width}`}AWS CloudFront
srcSetBuilder={(src, width) => `${src}?width=${width}`}Bunny CDN
srcSetBuilder={(src, width) => `${src}?width=${width}`}Enable beautiful image zoom with a single prop.
<SmartImage src="/shoe.jpg" alt="Running Shoe" zoom />By default, clicking the image opens a fullscreen lightbox.
React Smart Image supports four zoom experiences.
| Mode | Description |
|---|---|
| lightbox | Fullscreen modal |
| inline | Zoom inside image |
| magnifier | Magnifying lens |
| fullscreen | Browser Fullscreen API |
Perfect for galleries.
<SmartImage src="/product.jpg" zoom />Magnify the image without opening a popup.
<SmartImage
src="/product.jpg"
zoom
zoomOptions={{ mode: "inline", scale: 2 }}
/>Ideal for ecommerce product images.
<SmartImage
src="/product.jpg"
zoom
zoomOptions={{ mode: "magnifier", scale: 3, magnifierSize: 180 }}
/>Use the browser Fullscreen API.
<SmartImage src="/photo.jpg" zoom zoomOptions={{ mode: "fullscreen" }} />
⚠️ iOS Safari doesn't support the Fullscreen API on non-<video>elements. There, the lightbox still opens normally, but it silently stays windowed instead of entering true fullscreen.
Display a different image when zooming.
<SmartImage src="/thumb.jpg" zoom zoomSrc="/original-4000px.jpg" />Perfect for ecommerce product images.
Enable built-in controls.
<SmartImage
zoom
zoomOptions={{
showToolbar: true,
toolbarItems: ["zoomIn", "zoomOut", "reset", "download", "fullscreen"],
}}
/>
⚠️ Thedownloadbutton uses thedownloadattribute, which forces a save only for same-origin images (or cross-origin images served with the right CORS headers). Otherwise the browser may just open the image in a new tab instead.
Choose the animation style.
zoomOptions={{ animation: "scale" }}Available:
- fade
- scale
- zoom
- slide
- none
Control animation duration:
zoomOptions={{ animation: "scale", animationDuration: 500 }}Show a caption beneath the zoomed image.
<SmartImage src="/product.jpg" zoom zoomOptions={{ caption: true }} />caption:true uses the image's alt text automatically, or pass a custom string:
zoomOptions={{ caption: "Available in 4 colors" }}Customize the lightbox backdrop color and how it can be closed.
zoomOptions={{
backdropColor: "rgba(0,0,0,0.95)",
closeOnBackdropClick: false,
closeOnEsc: false,
showCloseButton: false,
}}| Option | Default |
|---|---|
| backdropColor | rgba(0,0,0,0.85) |
| closeOnBackdropClick | true |
| closeOnEsc | true |
| showCloseButton | true |
Apply a class name or inline style to the zoomed <img> inside the lightbox.
zoomOptions={{ className: "my-zoomed-image", style: { borderRadius: 8 } }}The lightbox is fully keyboard accessible.
- Open — click the image, or focus it and press Enter / Space
- Close — press Escape, click the × button, or click the backdrop (clicking the image itself keeps it open)
It renders through a React portal into <body> (so it escapes any overflow: hidden / transform ancestors), locks background scroll while open, moves focus to the close button, and exposes role="dialog" + aria-modal.
Avoid repeating the same props everywhere. Wrap your application once.
import { SmartImageProvider } from "@concatstring/react-smart-image";
function App() {
return (
<SmartImageProvider
defaults={{
responsive: true,
retry: 2,
placeholder: "blur",
transition: "fade",
}}
>
<AppRoutes />
</SmartImageProvider>
);
}Every SmartImage automatically inherits these defaults. Individual component props always override provider defaults.
The merge is shallow and applies at the top level only — an object prop like zoomOptions set on the image replaces the provider's zoomOptions entirely rather than merging field-by-field. Nesting SmartImageProviders is supported, but the closest provider's defaults (and presets) replace the outer one's rather than merging with it.
You can also set a default preset for every image, so preset doesn't need to be repeated on each one:
<SmartImageProvider defaults={{ preset: "product" }} presets={presets}>
<AppRoutes />
</SmartImageProvider>An explicit preset prop on a SmartImage always overrides this default.
| Prop | Type | Default | Description |
|---|---|---|---|
| defaults | SmartImageDefaults | {} | Default SmartImage props; may include preset for an app-wide default |
| presets | SmartImagePresetMap | {} | Image preset collection |
| children | ReactNode | — | React children |
Presets let you define reusable image configurations. Instead of repeating props across your application, define them once.
import { createImagePresets } from "@concatstring/react-smart-image";
const presets = createImagePresets({
hero: { priority: true, responsive: true, transition: "fade" },
product: { zoom: true, responsive: true, placeholder: "blur" },
avatar: { width: 60, height: 60, skeleton: true },
});Use a preset:
<SmartImage preset="hero" src="/hero.jpg" alt="Hero" />
<SmartImage preset="product" src="/shoe.jpg" alt="Running Shoe" />
<SmartImage preset="avatar" src="/user.jpg" alt="User" />Presets dramatically reduce repetitive code.
💡
keyof typeof presetsgives you a typed union of preset names (e.g.'hero' | 'product' | 'avatar') — handy for typing apresetprop on your own wrapper component.
⚠️ An unrecognizedpresetname logs a console warning and falls back to just the provider defaults + component props, rather than throwing.
They work together.
<SmartImageProvider defaults={{ retry: 2 }} presets={presets}>
<App />
</SmartImageProvider>Resolution order:
Component Props → Preset → Provider Defaults → Library Defaults
The closest value always wins.
Hero Banner
<SmartImage preset="hero" src="/hero.jpg" alt="Hero" />Product Card
<SmartImage preset="product" src="/shoe.jpg" alt="Running Shoe" />User Avatar
<SmartImage preset="avatar" src="/avatar.jpg" alt="User" />Blog Thumbnail
<SmartImage src="/blog.jpg" responsive lazy placeholder="blur" />Gallery Image
<SmartImage src="/gallery.jpg" zoom responsive skeleton transition="fade" />| Scenario | Recommended Features |
|---|---|
| Hero Banner | priority + responsive |
| Product Gallery | zoom + responsive + placeholder |
| User Avatar | skeleton |
| Dashboard | lazy |
| Ecommerce | responsive + zoom + retry |
| Blog | lazy + blur |
| Portfolio | thumbnail + zoom |
| Marketing Page | priority + aspectRatio |
| Slow Network | autoBlur + retry |
| CDN Images | responsive + srcSetBuilder |
✅ Use priority only for above-the-fold images.
✅ Use lazy for all remaining images.
✅ Enable responsive whenever possible.
✅ Use aspectRatio to prevent layout shift.
✅ Prefer format="auto" for modern image formats.
✅ Combine placeholder="blur" with responsive for the best perceived performance.
✅ Use presets to keep image configuration consistent across your application.
React Smart Image provides lifecycle callbacks for analytics, monitoring, and custom business logic.
Triggered once when the image enters the viewport.
Perfect for:
- Impression tracking
- Analytics
- Marketing events
- Lazy business logic
<SmartImage
src="/banner.jpg"
alt="Summer Sale"
lazy
onVisible={() => analytics.track("banner_impression")}
/>Notes
- Fires only once.
- Works with or without
lazy. - Uses the same Intersection Observer as lazy loading.
Get detailed information after an image successfully loads.
<SmartImage
src="/photo.jpg"
onLoadInfo={(info) => {
console.log(info.loadTime, info.width, info.height, info.fromCache);
}}
/>interface LoadInfo {
loadTime: number;
width: number;
height: number;
fromCache: boolean;
}Useful for:
- Performance monitoring
- Logging
- Analytics
- Debugging
Know when the zoom viewer opens or closes.
⚠️ Only fires forlightbox/fullscreenmodes. Hovering to triggerinline/magnifierzoom doesn't callonZoomChange.
<SmartImage zoom onZoomChange={(open) => console.log(open)} />Perfect for:
- Analytics
- Pause videos
- Stop autoplay
- Track product interactions
React Smart Image includes an in-memory cache to avoid unnecessary image processing.
import { clearImageCache } from "@concatstring/react-smart-image";
clearImageCache();Removes every cached image.
import { invalidateImageCache } from "@concatstring/react-smart-image";
invalidateImageCache("/uploads/avatar.jpg");Useful after:
- Uploading a new profile picture
- Replacing an image
- CDN cache refresh
All public types are exported.
import type {
SmartImageProps,
LoadInfo,
ResponsiveSizes,
ZoomOptions,
ZoomMode,
ZoomAnimation,
ZoomToolbarButton,
TransitionKind,
SmartImageDefaults,
SmartImageProviderProps,
SmartImagePresetConfig,
SmartImagePresetMap,
} from "@concatstring/react-smart-image";No additional packages required.
SmartImage renders a bare <img> whenever possible.
A <span> wrapper is added — for the component's entire lifetime, not just while loading — whenever one of these props is set:
skeletonplaceholder="blur"thumbnail
The wrapper stays mounted after the image finishes loading, so the same <img> node persists across the loading → loaded transition. This is required for transition (and the default crossfade) to animate correctly.
⚠️ In wrapper mode,classNameis applied to the<span>, not the inner<img>.
| Prop | Type | Default | Description |
|---|---|---|---|
| src | string | — | Image source URL |
| alt | string | "" | Accessible alternative text |
| width | number | string | — | Width |
| height | number | string | — | Height |
| preset | string | — | Apply preset configuration |
All standard <img> HTML attributes (className, style, onClick, onLoad, onError, loading, etc.) are forwarded to the underlying <img> element. ref is also forwarded via forwardRef.
| Prop | Type | Default | Description |
|---|---|---|---|
| lazy | boolean | false | Delay loading until the image enters the viewport. Ignored when priority is set |
| priority | boolean | false | Load with maximum priority — disables lazy, sets loading="eager" + fetchpriority="high", injects <link rel="preload"> |
| aspectRatio | number | string | — | Reserve layout space before load via CSS aspect-ratio. Prevents layout shift (improves CLS) |
| Prop | Type | Default | Description |
|---|---|---|---|
| skeleton | boolean | false | Requires width and height |
| placeholder | "blur" | — | Requires width and height |
| blurDataURL | string | — | Base64 LQIP source |
| autoBlur | boolean | false | Derives the blur preview from a tiny version of src |
| blurWidth | number | 24 | Width (px) of the auto-blur preview |
| thumbnail | string | — | Low-quality image shown while the full image loads |
| Prop | Type | Default | Description |
|---|---|---|---|
| transition | fade, scale, grow, rotate, flip, slide-up, slide-left, reveal, none | opacity crossfade in skeleton/blur mode, otherwise none |
Ignored when thumbnail is set |
| transitionDuration | number | 300 | Duration (ms) of the transition |
| fade | boolean | false | Shorthand for transition="fade" |
| Prop | Type | Default | Description |
|---|---|---|---|
| responsive | boolean | false | Enable auto-generated srcSet/sizes |
| strategy | srcset | viewport | srcset | srcset lets the browser pick (DPR-aware); viewport picks by media query only and ignores DPR |
| sizes | ResponsiveSizes | string | — | Breakpoint widths, or a raw sizes string passed through as-is |
| srcSetBuilder | Function | `${src}?w=${width}` |
Maps src + a breakpoint width to a URL. Reused by autoBlur |
interface ResponsiveSizes {
mobile?: number; // used for max-width: 640px
tablet?: number; // used for max-width: 1024px
desktop?: number; // default (no media query)
}| Prop | Type | Default | Description |
|---|---|---|---|
| avif | boolean | false | Try a .avif version of src first (checked before webp) |
| webp | boolean | false | Try a .webp version of src; falls back to the original on failure |
| format | auto | — | Shorthand for enabling both avif and webp |
| Prop | Type | Default | Description |
|---|---|---|---|
| retry | number | 0 | Number of times to retry a failed load |
| retryDelay | number | 1000 | Base delay (ms) between retries. Each retry doubles the delay (exponential backoff) |
| fallback | string | — | URL to display when the image fails to load after all retries |
| Prop | Type | Default | Description |
|---|---|---|---|
| zoom | boolean | false | Enable zoom. Clicking opens a lightbox by default; configure via zoomOptions |
| zoomSrc | string | src |
Separate (usually higher-res) image shown when zoomed |
| zoomOptions | ZoomOptions | — | Zoom configuration — see below |
| onZoomChange | Function | — | Called when the zoom lightbox opens (true) or closes (false). Only fires for lightbox/fullscreen modes |
| Prop | Type | Default | Description |
|---|---|---|---|
| mode | lightbox, inline, magnifier, fullscreen | lightbox | Trigger/presentation — lightbox/fullscreen open a popup; inline/magnifier magnify in place |
| animation | fade, scale, zoom, slide, none | fade | Lightbox open/close animation |
| animationDuration | number | 300 | Animation duration in ms |
| showToolbar | boolean | false | Show the lightbox toolbar |
| toolbarItems | ZoomToolbarButton[] | zoomIn, zoomOut, reset, download, fullscreen | Buttons to show, in order |
| scale | number | 2 | Magnification for inline/magnifier and toolbar zoom steps |
| magnifierSize | number | 160 | Lens diameter (px) in magnifier mode |
| backdropColor | string | rgba(0,0,0,0.85) | Lightbox backdrop color |
| caption | boolean | string | — | Caption under the zoomed image (true = use alt) |
| closeOnBackdropClick | boolean | true | Close on backdrop click |
| closeOnEsc | boolean | true | Close on Escape |
| showCloseButton | boolean | true | Show the × close button |
| className | string | — | Applied to the zoomed <img> |
| style | CSSProperties | — | Applied to the zoomed <img> |
| Prop | Type | Default | Description |
|---|---|---|---|
| onVisible | Function | — | Fires once when image enters viewport |
| onLoadInfo | Function | — | Returns load statistics |
| onZoomChange | Function | — | Zoom open / close callback. Only fires for lightbox/fullscreen modes |
Full usage and prop table: see SmartImageProvider above.
Create reusable image configurations.
const presets = createImagePresets({
hero: { priority: true },
});Returns a fully typed preset object.
| Browser | Supported |
|---|---|
| Chrome | ✅ |
| Edge | ✅ |
| Firefox | ✅ |
| Safari | ✅ |
| Mobile Chrome | ✅ |
| Mobile Safari | ✅* |
* zoomOptions={{ mode: "fullscreen" }} doesn't enter true fullscreen on iOS Safari — see Fullscreen.
Can I replace <img> directly?
Yes. React Smart Image is designed as a drop-in replacement.
Does it support SSR?
Yes. Compatible with SSR frameworks including:- Next.js
- Remix
- Gatsby
- Astro
- React Router SSR
Does it work with Vite?
Yes. No configuration required.Does it support React 19?
Yes. Supports React 18 and newer.Can I use Cloudinary?
Yes. Use `srcSetBuilder`.Can I use AWS CloudFront?
Yes. Use `srcSetBuilder`.Does it support ImageKit?
Yes.Does it support Imgix?
Yes.Can I disable animations?
Yes.transition="none"Can I use only Zoom?
Yes. Every feature is completely independent.Can I combine multiple features?
Absolutely.<SmartImage
responsive
zoom
lazy
retry={2}
placeholder="blur"
format="auto"
/>Contributions are welcome!
If you find a bug, have a feature request, or would like to improve the documentation, please open an issue or submit a pull request.
GitHub Issues
https://github.com/concatstring-account/react-smart-image/issues
See GitHub Releases for the latest updates.
MIT © Concatstring Labs
https://react-smart-image.netlify.app
https://labs.concatstring.com/products/react-smart-image
https://www.npmjs.com/package/@concatstring/react-smart-image
https://github.com/concatstring-account/react-smart-image
If React Smart Image helps your project, please consider:
⭐ Starring the GitHub repository
📦 Sharing the package
🐛 Reporting bugs
💡 Suggesting new features
Your support helps make the library even better for the React community.