Skip to content

nhonn/react-native-viewsnap

Repository files navigation

react-native-viewsnap

Native-quality view capture for React Native’s New Architecture, powered by Nitro Modules.

Viewsnap clones the behaviour of react-native-view-shot, but ships as a Nitro module that only targets the Fabric renderer for maximal performance and minimal bridge overhead.


Features

  • 🚀 Fabric-only implementation – no legacy bridge fallback.
  • 📦 Tree-shakable API (captureRef + useViewsnap), no side effects on import.
  • 🎯 Imperative control via captureRef(ref, options).
  • 🪝 Declarative ergonomics with useViewsnap.
  • 🎨 Configurable output: PNG/JPEG/WebP (Android), base64/data URI/tmpfile.
  • 🖼️ High-resolution captures with custom scaling and size overrides.
  • Optimised rendering: skips redundant redraws, optional clipping relaxation.

Requirements

  • React Native 0.72+ with New Architecture (Fabric) enabled.
  • Nitro Modules properly installed in the host app.
  • iOS 13+ / Android API 21+ (WebP output uses Android API 30+; falls back to PNG otherwise).

❗ Viewsnap does not support the legacy architecture – RCT_NEW_ARCH_ENABLED=1 must be set for builds.


Installation

npm install react-native-viewsnap
# or
yarn add react-native-viewsnap

Ensure Nitro Modules are initialised inside your app following the official guide.

After installing dependencies:

cd ios && pod install

On Android, sync Gradle or rebuild the project so the generated Nitro sources are compiled.


Usage

captureRef

import { captureRef } from 'react-native-viewsnap'
import { useRef } from 'react'
import { View } from 'react-native'

export function SnapshotExample() {
  const targetRef = useRef<View>(null)

  async function onCapture() {
    const uri = await captureRef(targetRef, {
      format: 'jpg',
      quality: 0.85,
      result: 'tmpfile'
    })
    console.log('Snapshot stored at:', uri)
  }

  return (
    <View ref={targetRef}>
      {/* ... */}
    </View>
  )
}

useViewsnap

import { useViewsnap } from 'react-native-viewsnap'
import { useCallback, useRef } from 'react'
import { Pressable, View } from 'react-native'

export function CaptureButton() {
  const ref = useRef<View>(null)
  const snap = useViewsnap({ format: 'png', result: 'data-uri' })

  const handlePress = useCallback(async () => {
    const dataUri = await snap(ref)
    // do something with dataUri
  }, [snap])

  return (
    <>
      <View ref={ref} />
      <Pressable onPress={handlePress}>{/* ... */}</Pressable>
    </>
  )
}

Both APIs accept either a numeric native tag or a ref created via useRef, createRef, or legacy class component refs.


Options

Option Type Default Description
format 'png' | 'jpg' | 'webp' 'png' Output encoding. WebP falls back to PNG on iOS or when unsupported.
quality number (0-1) 1 Compression quality for lossy formats (jpg, webp). Ignored for PNG.
result 'tmpfile' | 'base64' | 'data-uri' 'tmpfile' Return type: temp file path, base64 string, or data URI.
fileName string Random UUID Custom filename (without extension) when result="tmpfile".
width / height number Captured view size × scale Output dimensions in physical pixels. Preserves aspect ratio when only one is provided.
scale number PixelRatio.get() (JS) / device density (native) Additional scale multiplier applied before rendering. Must be > 0.
includeNonVisibleSubviews boolean true Disable parent clipping while rendering to include overflowing children.
skipRedraw boolean false When true, skips drawHierarchyAfterUpdates / invalidate to save work if the view is already up to date.

Tree Shaking

  • package.json sets "sideEffects": false.
  • Nitro module creation is lazy – importing react-native-viewsnap does not register native code until captureRef/useViewsnap runs.

This allows bundlers (Metro, Webpack) to drop any unused exports automatically.


Regenerating Nitro Bindings

Whenever you edit src/specs/Viewsnap.nitro.ts, regenerate bindings:

npx tsc --noEmit
npx nitrogen --logLevel=debug

Commit the updated files under nitrogen/generated/ alongside your source changes.


Native Implementation Notes

  • iOS: Resolves Fabric UIView via RCTSurfacePresenter, captures with UIGraphicsImageRenderer, and writes to disk or encodes in-memory as requested.
  • Android: Resolves FabricUIManager, captures on the UI thread into a scaled Bitmap, and encodes using Bitmap.compress.
  • Both variants relax clipping when includeNonVisibleSubviews is true, and validate scale/bounds to fail fast with descriptive errors.

Troubleshooting

  • Viewsnap module is not available – ensure Nitro Modules are initialised before JS runs and the app is rebuilt after installation.
  • Fabric SurfacePresenter is unavailable / NewArchitectureOnly – check that New Architecture is enabled (hermes, fabric, useFabric flags, RCT_NEW_ARCH_ENABLED=1).
  • Blank captures – confirm the ref resolves to a mounted host view and optionally disable skipRedraw.

License

MIT © 2025 Margelo / Maintainers. See LICENSE if present or the package metadata for details.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published