npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@bhavesh2706/react-native-smart-image

v2.1.0

Published

Lightweight React Native image display with caching and opt-in UX flags — Expo and bare CLI. Powered by React Native Image. Zero runtime dependencies.

Readme

@bhavesh2706/react-native-smart-image

npm version CI license: MIT

Focused image display for React Native (Expo & bare) on iOS and Android.

Demo screen recordings above live in media/ for GitHub only — they are not shipped in the npm tarball. The example app uses platform-specific GIFs under example/assets/demo.{ios,android,}.gif (Metro picks the right one).

  • Load images from URL (also require(), data:, file://, content://, ph://)
  • Cache via React Native Image.prefetch + in-memory known keys
  • Opt-in flags — loadingIndicator, fullscreenOnPress, pinchZoom, compress (prop → provider config → package default)
  • Themes — light / dark / system via SmartImageProvider, token-driven chrome
  • Common formats through the OS / RN Image (JPEG, PNG; WebP/GIF/HEIC where the OS allows)

This package is not an editor, picker, uploader, or ML toolkit — it is display + cache + optional UX flags.

Lean deps: runtime dependencies stay empty. Required peers: react + react-native (>=0.72.0) only — no expo-image. Optional peers: @react-native-community/netinfo, expo-file-system. Missing optional modules degrade gracefully — they never throw.

npm · GitHub · Issues · YouTube Shorts

Why no expo-image (v2)

v2 uses React Native’s built-in Image so apps do not need an extra image npm package or install-expo-modules solely for this library.

| Kept | Weakened / lost vs expo-image | |------|-------------------------------| | Display URL / require / data / file | Disk cache path / size / clear disk APIs | | Flags, themes, fullscreen, pinch | Blurhash/thumbhash native decode | | Prefetch via Image.prefetch | Advanced cachePolicy tiers | | JPEG/PNG (GIF/WebP OS-dependent) | HEIC / recycling / priority / downscale | | Empty dependencies | Cross-dissolve transition / onProgress |

Unsupported props (recyclingKey, priority, allowDownscale, blurhash engine placeholder, etc.) are accepted and degrade gracefully.

Feature flags (dynamic + defaults)

Everything optional is a flag. Bare <SmartImage source={…} /> only displays + caches. Turn extras on when you need them — per image, or once via the provider.

| Flag | Package default | What it does | |------|-----------------|--------------| | loadingIndicator | false | ActivityIndicator while loading | | fullscreenOnPress | false | Tap → full-screen lightbox | | pinchZoom | true | Pinch / double-tap in fullscreen (only if fullscreen is on) | | compress | off | URL rewrite before fetch (lighter remote bytes) |

Resolution order: prop → SmartImageProvider config → package default.

Install

Expo

npx expo install @bhavesh2706/react-native-smart-image
# optional
npx expo install @react-native-community/netinfo expo-file-system

Optional config plugin (always adds androidx.exifinterface so HEIC may decode on some Android without crashing Fresco; photo library / storage permissions stay opt-in):

{
  "expo": {
    "plugins": ["@bhavesh2706/react-native-smart-image"]
  }
}

Add { "photoLibrary": true } (or readExternalStorage) only if you load privileged photo-library / content:// URIs:

{
  "expo": {
    "plugins": [["@bhavesh2706/react-native-smart-image", { "photoLibrary": true }]]
  }
}

| Surface | What works | | --- | --- | | Expo Go / web | Display, prefetch, flags, themes. | | Dev build / bare | Full set, including optional peers when installed. |

React Native CLI (bare)

npm install @bhavesh2706/react-native-smart-image
# no expo-image / install-expo-modules required for this package
cd ios && pod install && cd ..   # only if your app already uses pods

Optional peers are the same (netinfo, expo-file-system).

If you load HEIC / HEIF on Android, add this to android/app/build.gradle (Fresco’s HeifExifUtil otherwise throws NoClassDefFoundError on an OkHttp thread and kills the process):

implementation("androidx.exifinterface:exifinterface:1.3.7")

The Expo config plugin injects the same line on prebuild.

Tested with: React 19.2.3 · React Native 0.87.1 (library root / bare CLI) · Expo SDK 57 / RN 0.86.3 (example). The example stays on SDK 57 until Expo ships a stable release with RN ≥0.87 (Expo 58 preview currently targets RN 0.88 RC, not 0.87.1). New Architecture is the primary target.

Quick start

import { SmartImage, SmartImageProvider } from '@bhavesh2706/react-native-smart-image';

export default function App() {
  return (
    <SmartImageProvider config={{ cachePolicy: 'memory-disk' }}>
      <SmartImage
        source="https://example.com/photo.jpg"
        style={{ width: 200, height: 150 }}
        contentFit="cover"
        loadingIndicator // opt-in spinner
      />
    </SmartImageProvider>
  );
}

Recipes

Per-image flags

<SmartImage source={url} style={styles.img} />
<SmartImage source={url} loadingIndicator />
<SmartImage source={url} compress={{ maxWidth: 400 }} />
<SmartImage source={url} fullscreenOnPress pinchZoom />

App-wide defaults (still overridable per image)

<SmartImageProvider
  config={{
    loadingIndicator: true,
    fullscreenOnPress: true,
    cachePolicy: 'memory-disk',
  }}
>
  <App />
</SmartImageProvider>

Caching

await SmartImage.prefetch(['https://…/a.jpg', 'https://…/b.jpg']);
await SmartImage.isCached(url); // session memory set (after prefetch/load)
await SmartImage.clearCache();  // clears known keys (no native disk clear)
await SmartImage.getCachePath(url); // always null with RN Image
await SmartImage.getCacheSize(); // typically null — RN Image has no disk cache API

Policies (immutable · web · memory-disk · …) remain in the public API for compatibility; RN Image does not enforce native cache tiers.

Compress before display (opt-in, zero extra deps)

True on-device re-encode would need a native package — we don’t add one. Instead compress lightens what is fetched:

  1. Host-aware URL rewrite (Picsum, Unsplash, Placehold.co, Cloudinary)
  2. Your CDN via config.cdn.transform (or createQueryCdnTransform())
<SmartImage
  source="https://picsum.photos/id/1015/900/600"
  compress={{ quality: 0.6, maxWidth: 400 }}
  onCompressed={(r) => console.log(r.strategy, r.uri)}
/>

// imperative (sync)
const { uri, compressed } = SmartImage.compress(url, { maxWidth: 800 });

compress={true} → { quality: 0.7 }. Unknown hosts are left unchanged unless you set queryParams: true or configure a CDN.

Theme + dark mode

import {
  SmartImageProvider,
  useSmartColorScheme,
} from '@bhavesh2706/react-native-smart-image';

<SmartImageProvider
  mode="system" // or 'light' | 'dark'
  theme={{
    light: { colors: { accent: '#4F46E5' } },
    dark: { colors: { accent: '#818CF8' } },
  }}
>
  <App />
</SmartImageProvider>

Props (SmartImage)

| Prop | Type | Default | Notes | |------|------|---------|-------| | source | string | { uri } | require() | … | — | Required. URL, headers object, data URI, local URI, … | | loadingIndicator | boolean | false | Opt-in ActivityIndicator (or provider config) | | fullscreenOnPress | boolean | false | Opt-in tap → lightbox (or provider config) | | pinchZoom | boolean | true | Pinch / pan / double-tap when fullscreen is on | | compress | boolean | options | off | Opt-in lighter fetch (URL rewrite) | | onCompressed | (result) => void | — | Callback after compress rewrite | | placeholder | 'skeleton' | 'shimmer' | image | — | Loading placeholder (blurhash → solid/overlay only) | | cachePolicy | CachePolicy | provider / web | API-compatible; not enforced by RN Image | | contentFit | ContentFit | provider / cover | Mapped to resizeMode | | showRetry | boolean | — | Retry control on error | | recyclingKey | string | — | Accepted; ignored by RN Image | | priority | 'low' | 'normal' | 'high' | — | Decode queue only (not native fetch priority) | | onPress / onFullscreenOpen / onFullscreenClose | callbacks | — | Press + lightbox lifecycle |

Formats (iOS & Android)

SmartImage displays whatever React Native Image + the OS can decode. JPEG / JPG / PNG are guaranteed on both iOS and Android.

| Format | iOS | Android | Notes | |--------|-----|---------|--------| | JPEG / JPG | ✅ | ✅ | Same codec; .jpg and .jpeg both work | | PNG | ✅ | ✅ | Transparency when supported | | HEIC / HEIF | ⚠️ | ⚠️ | May decode on some Android. Apps must ship androidx.exifinterface (Expo plugin and the example Gradle file add it) or Fresco crashes with NoClassDefFoundError: ExifInterface while reading orientation | | WebP | ⚠️ | ✅ | OS / RN version dependent | | GIF | ✅ | ✅ | Follows OS defaults (autoplay accepted, not controlled) | | AVIF | ⚠️ | ⚠️ | OS / browser dependent | | BMP | ✅ | ✅ | | | ph:// / assets-library:// | ⚠️ | — | iOS Photos library URIs when OS allows | | data:image/… | ✅ | ✅ | Base64 |

iOS upload helper

prepareImageForUpload resolves URI + MIME / fileName only (zero new deps). It does not re-encode HEIC → JPEG.

import { prepareImageForUpload, IOS_UPLOAD_FORMATS } from '@bhavesh2706/react-native-smart-image';

const prepared = await prepareImageForUpload(asset.uri);
formData.append('file', {
  uri: prepared.uri,
  type: prepared.mimeType,
  name: prepared.fileName,
} as any);

Large lists (thousands of images)

Use a virtualized list — do not put 1000+ images in a plain ScrollView.

<SmartImageProvider config={{ maxConcurrentDecodes: 6 }}>
  <FlatList
    data={items}
    keyExtractor={(item) => item.id}
    windowSize={5}
    maxToRenderPerBatch={8}
    removeClippedSubviews
    renderItem={({ item }) => (
      <SmartImage
        source={item.url}
        style={{ width: 120, height: 120 }}
        recyclingKey={item.id}
        priority="low"
      />
    )}
  />
</SmartImageProvider>

recyclingKey / priority still help SmartImage’s decode queue; they are not native FastImage-style controls.

Platform support

| Capability | iOS | Android | Expo Go | Web | | --- | :---: | :---: | :---: | :---: | | Display + prefetch + flags | ✅ | ✅ | ✅ | ✅ | | Fullscreen + pinch zoom | ✅ | ✅ | ✅ | ✅ | | HEIC / ph:// | ⚠️ | ⚠️ | ⚠️ | — | | Offline / netinfo | ✅¹ | ✅¹ | ✅¹ | ✅¹ | | Disk cache path / byte size | — | — | — | — |

¹ Optional @react-native-community/netinfo. Optional expo-file-system helps privileged URI helpers (ph:// / content://), not SmartImage disk bytes — RN Image has no disk cache API (getCachePath / getCacheSize stay null).

Tested with: React 19.2.3 · React Native 0.87.1 (library / bare CLI) · Expo SDK 57 / RN 0.86.3 (example — stays until stable Expo ships RN ≥0.87).

Other exports

SmartImageProvider · useSmartTheme / useSmartConfig / useSmartColorScheme · useSmartImage / useSmartImageCache / useNetworkStatus · createQueryCdnTransform · format helpers (guessImageFormat, isFormatSupported, describeSupportedFormats, CORE_IMAGE_FORMATS, IOS_UPLOAD_FORMATS) · prepareImageForUpload / isHeicUri · theme defaults (lightTheme, darkTheme, defaultThemes)

Example app

Kitchen-sink with 14 sections — see example/README.md:

cd example && npm install
npm run android   # device or emulator
npm run ios       # simulator

Highlights:

  • Provider config turns on loadingIndicator + fullscreenOnPress for the demo
  • Section 2 plays a bundled local GIF via Metro platform resolution (demo.ios.gif / demo.android.gif / demo.gif for web)
  • Section 12 cache toolkit: prefetch / isCached work; Clear resets in-memory keys (no disk byte size)
  • Safe area via react-native-safe-area-context

Hero GIFs in media/smart_Image_iOS.gif and media/smart_Image_android.gif are the matching platform recordings (GitHub docs only; not in the npm package).

Contributing

See CONTRIBUTING.md. Conventional Commits; CI runs typecheck, lint, format, tests, build, and size-limit. Keep the peer surface lean (react + react-native required; optional peers only when needed) and prefer opt-in flags with defaults for new UX.

License

MIT · Bhavesh Barot · npm · YouTube