GuidesRecipes

Recipes

<VideoThumbnail> — the drop-in tile

A ready-made, fully customizable thumbnail tile is shipped with the package:

import { VideoThumbnail } from 'react-native-nitro-thumbnail';
 
<VideoThumbnail
  videoUrl={item.videoUrl}
  serverThumbnail={item.thumbnailUrl}   // shown directly if present
  isLoading={item.isFetching}           // your API loading state
  onPress={() => openPlayer(item.videoUrl)}
/>;

It handles the whole lifecycle:

serverThumbnail provided?  ──yes──▶  show it          (createThumbnail never runs)
        │ no

   generate it  ──▶  createThumbnail({ url })  ──▶  cache → show + play button
  • Server-first — if serverThumbnail is set, it’s shown and nothing is generated.
  • Shimmer while loading — built-in Animated sweep (iOS + Android + Web), or pass your own renderLoading. Also driven by your isLoading prop.
  • Play button overlay — customizable color/size, or fully custom via renderPlayButton.
  • Graceful failure — a broken video shows an empty/placeholder state, never a crash.
  • Zero extra dependencies.
⚠️

“Play” is a callback, not a bundled player. onPress fires when the tile is tapped — you open your video player / navigate. The library never depends on a video-player package, so it stays small and unopinionated.

Props

All props are optional except a source (videoUrl and/or serverThumbnail).

PropTypeDefaultDescription
videoUrlstring | nullVideo to generate from when there’s no serverThumbnail.
serverThumbnailstring | nullA thumbnail URL from your API. If set, shown directly — no generation.
timeStampnumber1000Frame to capture (ms) when generating.
format / maxWidth / maxHeight / headers / cacheNamePassed straight to createThumbnail.
isLoadingbooleanfalseForce the loading (shimmer) state, e.g. while your API fetches.
renderLoading() => ReactNodeReplace the built-in shimmer.
shimmerColors[string, string]['#e2e8f0','#f8fafc'][base, highlight] for the built-in shimmer.
shimmerDurationnumber1200Shimmer sweep duration (ms).
showPlayButtonbooleantrueShow the play overlay (requires onPress).
onPress() => voidTile pressed — open your player here.
renderPlayButton() => ReactNodeReplace the built-in play button.
playButtonColor / playButtonBackgroundColor / playButtonSizestring / string / number#fff / rgba(0,0,0,0.45) / 56Theme the default play button.
width / height / borderRadius / resizeMode / backgroundColor—/—/12/'cover'/#e2e8f0Appearance.
style / imageStyleStylePropEscape hatches for full styling.
renderError() => ReactNodeCustom error state.
onThumbnail / onErrorcallbacksFired when a thumbnail is generated / fails.

In a list

import { FlatList } from 'react-native';
import { VideoThumbnail } from 'react-native-nitro-thumbnail';
 
<FlatList
  data={videos}
  keyExtractor={(v) => v.id}
  renderItem={({ item }) => (
    <VideoThumbnail
      width={160}
      height={90}
      videoUrl={item.videoUrl}
      serverThumbnail={item.thumbnailUrl}
      onPress={() => navigation.navigate('Player', { url: item.videoUrl })}
    />
  )}
/>;

Customizing it

// Dark shimmer + branded play button
<VideoThumbnail
  videoUrl={url}
  shimmerColors={['#1f2937', '#374151']}
  playButtonColor="#e0218a"
  playButtonSize={64}
  borderRadius={16}
  onPress={open}
/>
 
// Bring your own everything
<VideoThumbnail
  videoUrl={url}
  renderLoading={() => <MyGradientShimmer />}
  renderPlayButton={() => <MyPlayIcon />}
  onPress={open}
/>

A runnable version of all four cases (server / remote / local / failure) plus the shimmer toggle is in the example app.

Doing it yourself (headless)

If you’d rather build your own UI, the rule is the same — call createThumbnail only when there’s no server thumbnail:

const uri = serverThumbnail ?? generated;
React.useEffect(() => {
  if (serverThumbnail || !videoUrl) return; // server has one → do nothing
  createThumbnail({ url: videoUrl, cacheName: `vt-${videoUrl}` }).then((t) =>
    setGenerated(t.path)
  );
}, [videoUrl, serverThumbnail]);