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

react-native-nitro-thumbnail

v0.1.4

Published

Generate thumbnails from local or remote videos. Nitro-powered, pure Swift & Kotlin, New Architecture, iOS + Android + Web.

Downloads

124

Readme

🎬 react-native-nitro-thumbnail

Generate a thumbnail from any video — local or remote — with one async call.

The same API on iOS, Android & Web. Powered by Nitro: pure Swift & Kotlin, New Architecture, no bridge.

npm version npm downloads bundle size license TypeScript

platforms New Architecture powered by Nitro PRs welcome

📖 Read the Docs  ·  🚀 Quick Start  ·  🧰 API  ·  🔀 Migrate  ·  🛡️ Issues Solved

import { createThumbnail } from 'react-native-nitro-thumbnail';

const thumb = await createThumbnail({ url: 'https://media.example.com/clip.mp4' });
// → { path: 'file:///…/thumb.jpg', size: 19856, mime: 'image/jpeg', width: 512, height: 288 }

<Image source={{ uri: thumb.path }} />

That's the whole API. One function. It works the same whether the video is a file:// on disk or an https:// URL behind auth headers — and whether you're on an iPhone, an Android tablet, or in a browser.


🎥 See it in action

The frame createThumbnail() extracts{ timeStamp: 2000, maxWidth: 1280 }

Running on iOS  ▶︎ clip

Demo clip: Sintel © Blender Foundation, CC-BY 3.0.


✨ Features

| | | |---|---| | 🧩 One API, three engines | iOS (AVFoundation), Android (MediaMetadataRetriever), Web (<video>+<canvas>) behind one typed function. | | 🌐 Local & remote | file://, absolute paths, content:// (Android), or http(s) URLs — streamed and decoded directly, no manual download. Custom headers supported. | | ⚡ Built on Nitro | Pure Swift & Kotlin over JSI — no Obj-C/Java bridge, no JSON marshalling. The native contract is generated from one TS spec. | | 🧵 Off the main thread | Every call runs async on a background thread, so batch generation never janks your UI. | | 🎯 Typed errors | Failures reject with a ThumbnailError carrying a typed .code (FILE_NOT_FOUND, REMOTE_FETCH_FAILED, …). | | 💾 Built-in caching | Deterministic filenames skip re-decoding; a size cap evicts old thumbnails (LRU). Writes to the cache dir — never your gallery. | | 🖼️ Ready-made UI (optional) | A shipped, fully-customizable <VideoThumbnail> — server-first, shimmer loading, play button, onPress to open your player. Zero extra deps. | | 🔀 Drop-in replacement | Matches react-native-create-thumbnail — usually a one-line import change. | | 🛡️ Solves the known issues | Built to avoid the reported failure modes of older wrappers. |


🗺️ How it works

One TypeScript function validates your input and applies defaults, then calls a Nitro HybridObject implemented natively per platform. The box labelled "your app" never changes — only the engine behind it does.

📖 The complete request lifecycle — cache check, decode, encode, write, evict — with sequence diagrams, lives in the Architecture guide.


📦 Installation

npm install react-native-nitro-thumbnail react-native-nitro-modules
# or: yarn add react-native-nitro-thumbnail react-native-nitro-modules

react-native-nitro-modules is a required peer dependency (the Nitro runtime).

iOS — install pods:

cd ios && pod install

Expo — needs an Expo dev build (not Expo Go). No config plugin required:

npx expo install react-native-nitro-thumbnail react-native-nitro-modules
npx expo prebuild && npx expo run:ios   # or run:android

Requires React Native 0.75+ with the New Architecture enabled.


🚀 Quick start

import { createThumbnail, ThumbnailError } from 'react-native-nitro-thumbnail';

async function makeThumb(videoUri: string) {
  try {
    const thumb = await createThumbnail({
      url: videoUri,     // 'file:///…', 'content://…' (Android), or 'https://…'
      timeStamp: 1000,   // grab the frame at 1.0s (milliseconds)
      format: 'jpeg',    // 'jpeg' | 'png'
      maxWidth: 512,     // fit within 512×512, aspect preserved, never upscaled
      maxHeight: 512,
      quality: 0.9,      // jpeg quality 0..1 (ignored for png)
    });

    return thumb.path;   // → <Image source={{ uri: thumb.path }} />
  } catch (e) {
    if (e instanceof ThumbnailError) {
      console.warn(`thumbnail failed [${e.code}]: ${e.message}`);
    }
    throw e;
  }
}

📱 Platform support

| Platform | Engine | Minimum | Notes | |---|---|---|---| | 🍎 iOS | AVAssetImageGenerator | iOS 13 | async image(at:) on iOS 16+, copyCGImage fallback below | | 🤖 Android | MediaMetadataRetriever | minSdk 24 | getScaledFrameAtTime on API 27+, scale-after fallback below | | 🌐 Web | <video><canvas>toBlob | modern browsers | resolved automatically via index.web.ts |


🧰 API

createThumbnail(options: CreateThumbnailOptions): Promise<Thumbnail>

| Option | Type | Default | Description | |---|---|---|---| | url | string | required | Local file:///absolute path, content:// (Android), or http(s) URL. | | timeStamp | number | 0 | Frame time in milliseconds. | | format | 'jpeg' \| 'png' | 'jpeg' | Output format. | | maxWidth | number | 512 | Max width — aspect preserved, never upscaled. | | maxHeight | number | 512 | Max height — aspect preserved, never upscaled. | | quality | number | 0.9 | JPEG quality 0..1 (clamped). Ignored for PNG. | | cacheName | string | — | Deterministic filename; existing file returned without re-decoding. | | dirSize | number | 100 | Cache cap in MB (LRU eviction). | | headers | Record<string,string> | — | HTTP headers for remote fetches. | | timeToleranceMs | number | 2000 | How far from timeStamp a frame may be picked (0 = exact). | | onlySyncedFrames | boolean | true | Prefer the nearest keyframe (faster). |

interface Thumbnail {
  path: string;   // native: file:// URL · web: blob: object URL
  size: number;   // file size in bytes
  mime: string;   // 'image/jpeg' | 'image/png'
  width: number;  // actual output width  (≤ maxWidth)
  height: number; // actual output height (≤ maxHeight)
}

On native, path is a file:// URL in the app cache dir. On web it's an object URL — call URL.revokeObjectURL(path) when done.

INVALID_URL · FILE_NOT_FOUND · REMOTE_FETCH_FAILED · DECODE_FAILED · UNSUPPORTED_FORMAT · WRITE_FAILED · UNKNOWN

Every failure is a typed ThumbnailError extends Error. Full descriptions in the Error Handling guide.

📖 Full reference with recipes → API docs


🖼️ <VideoThumbnail> — optional ready-made tile

Don't want to wire up the UI yourself? Import the shipped component. It shows a server thumbnail if you have one, otherwise generates one (cached), with a shimmer while loading and a play button — fully themeable, zero extra dependencies.

import { VideoThumbnail } from 'react-native-nitro-thumbnail';

<VideoThumbnail
  videoUrl={item.videoUrl}
  serverThumbnail={item.thumbnailUrl}  // shown directly if present — no generation
  isLoading={item.isFetching}          // your API loading → shimmer
  onPress={() => openPlayer(item.videoUrl)}  // you open the player
  playButtonColor="#fff"
  shimmerColors={['#222', '#333']}
/>;

"Play" is your callback, not a bundled player — the library never depends on a video-player package. Full props + customization in the Recipes guide.

🛡️ Built to solve the known issues

This library was written knowing the common failure modes of video-thumbnail wrappers. The pain points reported against older libraries are solved by design:

  • No Android build breakage — Kotlin (no checked-IOException), modern AGP, no jcenter, no Apache Commons.
  • Remote URLs never crash — network failures reject with a typed REMOTE_FETCH_FAILED.
  • No UI jank — decoding runs off the main thread via Nitro Promise.async.
  • content:// gallery videos work on Android.
  • No gallery pollution — thumbnails go to the cache dir, not MediaStore.
  • Typed errors, never a silent null.

📖 See the full issue-by-issue breakdown → Issues Solved


💡 Examples

const thumb = await createThumbnail({
  url: 'https://media.example.com/clips/abc.mp4',
  headers: { Authorization: `Bearer ${token}` },
  timeStamp: 2000,
});
// First call decodes and writes thumbnails/poster-42.jpg
await createThumbnail({ url, cacheName: 'poster-42' });
// Subsequent calls (even after restart) return it instantly — no decode
const cached = await createThumbnail({ url, cacheName: 'poster-42' });
try {
  await createThumbnail({ url });
} catch (e) {
  if (e instanceof ThumbnailError && e.code === 'REMOTE_FETCH_FAILED') {
    // offer a retry
  }
}

There's a runnable demo (local + remote, on a button tap) in example/.


📚 Documentation

Everything lives on the documentation site →

| | | | |---|---|---| | 🏛️ Architecture | 📖 API Reference | ⚠️ Error Handling | | 💾 Caching | 🧑‍🍳 Recipes | 🔀 Migration | | 🛡️ Issues Solved | 🍎 iOS | 🤖 Android | | 🌐 Web | | |


🤝 Contributing

PRs of every size are welcome — a great first one is improving a doc you found confusing.

yarn            # install (Yarn 4 workspaces)
yarn nitrogen   # regenerate native specs after editing *.nitro.ts
yarn test       # Jest (TypeScript layer)

If this saved you time, a ⭐ on the repo helps others find it.


📄 License

MIT © contributors