react-native-view-screenshot
v1.0.0
Published
Supplementary screenshot utilities for React Native — metadata, annotations, redaction, galleries, multi-screen sequences, diff configs, snapshot testing helpers, HTML/Markdown reports
Maintainers
Readme
react-native-view-screenshot
Supplementary screenshot utilities for React Native.
Higher-level screenshot workflows on top of react-native-view-capture — metadata, annotations, redaction masks, diff configs, album galleries, multi-screen sequences, storyboards, snapshot testing helpers, HTML/Markdown reports, privacy policies, and accessibility alt-text.
npm install react-native-view-screenshotZero runtime dependencies. Pure JavaScript — works in Node.js, React Native, and test runners.
Metadata
import { buildScreenshotMeta, formatMetadata, buildScreenshotFilename, parseScreenshotFilename } from 'react-native-view-screenshot';
const meta = buildScreenshotMeta({
screen: 'HomeScreen',
component: 'FeedList',
platform: 'ios',
deviceModel: 'iPhone 15 Pro',
osVersion: '17.2',
appVersion: '3.1.0',
locale: 'en-US',
colorScheme: 'dark',
tags: ['regression', 'sprint-42'],
});
formatMetadata(meta)
// 'screen=HomeScreen;component=FeedList;platform=ios;deviceModel=iPhone%2015%20Pro;...'
buildScreenshotFilename({ prefix: 'reg', screen: 'HomeScreen', scenario: 'dark mode', format: 'png' })
// 'reg_homescreen_dark-mode_1718450000000.png'
parseScreenshotFilename('reg_homescreen_dark-mode_1718450000000.png')
// { prefix: 'reg', screen: 'homescreen', scenario: 'dark-mode', timestamp: 1718450000000, ext: 'png' }Annotations
Build overlay descriptors for annotated screenshots (tutorial flows, bug reports, QA notes):
import { buildHighlight, buildArrow, buildLabel, buildCallout, buildStepBadge, buildAnnotationSet } from 'react-native-view-screenshot';
const annotations = buildAnnotationSet([
buildHighlight(40, 120, 300, 60, { color: '#ff3b30', label: 'Tap here' }),
buildArrow({ x: 200, y: 80 }, { x: 200, y: 120 }, { color: '#ff3b30' }),
buildLabel('Step 1: Tap the button', 40, 90, { background: '#ff3b30', fontSize: 13 }),
buildCallout('Sign in here', { x: 190, y: 150 }, { x: 320, y: 80 }),
buildStepBadge(1, 40, 130, { background: '#007aff' }),
]);Annotation types
| Function | Produces |
|---|---|
| buildHighlight(x, y, w, h, opts?) | Coloured border/fill rectangle |
| buildArrow(from, to, opts?) | Arrow between two points |
| buildLabel(text, x, y, opts?) | Text label with background chip |
| buildCallout(text, target, bubble, opts?) | Bubble connected to a point |
| buildStepBadge(step, x, y, opts?) | Numbered circle badge |
| buildRedaction(x, y, w, h, opts?) | Privacy blackout / blur / pixelate |
Redaction & privacy
import { buildRedaction, buildRedactionMask, autoRedactSensitive, buildScreenshotPolicy } from 'react-native-view-screenshot';
// Manual redaction rect
buildRedaction(50, 200, 280, 40, { mode: 'blur', blurRadius: 12 })
// Redact a list of regions
buildRedactionMask([
{ x: 50, y: 200, width: 280, height: 40 },
{ x: 50, y: 260, width: 180, height: 40 },
], { mode: 'blackout' })
// Auto-detect PII fields from a UI layout description
const fields = [
{ name: 'passwordInput', x: 40, y: 300, width: 300, height: 44 },
{ name: 'emailField', x: 40, y: 240, width: 300, height: 44 },
{ name: 'firstName', x: 40, y: 180, width: 140, height: 44 },
];
autoRedactSensitive(fields)
// → RedactionAnnotation[] for password + email (firstName is not matched)
// Privacy policy
buildScreenshotPolicy({ autoRedact: true, retentionDays: 3, allowSharing: false })Diff & snapshot testing
import { buildDiffConfig, diffThreshold, buildSnapshotRecord, buildSnapshotId, buildSnapshotConfig, buildDetoxExpectation, buildDiffResult, summariseDiffResults } from 'react-native-view-screenshot';
// Diff config
buildDiffConfig(baselineUri, currentUri, {
threshold: diffThreshold('strict'), // 0.001
outputDiffUri: '/tmp/diff.png',
ignoreRegions: [{ x: 0, y: 0, width: 375, height: 44 }], // ignore status bar
})
diffThreshold('strict') // 0.001
diffThreshold('normal') // 0.01
diffThreshold('relaxed') // 0.05
diffThreshold('animation') // 0.1
// Snapshot IDs for test filenames
buildSnapshotId('LoginScreen', 'empty form', { platform: 'ios', colorScheme: 'light' })
// 'LoginScreen__empty-form__ios__light'
// Snapshot records
buildSnapshotRecord('LoginScreen__empty-form__ios__light', uri, meta)
// Jest/Detox config
buildSnapshotConfig({ snapshotsDir: '__screenshots__', failureThreshold: 0.01 })
buildDetoxExpectation('LoginScreen__empty-form', { threshold: 0.01 })
// Diff results
const results = [
buildDiffResult(baseA, curA, { passed: true, diffPercent: 0.002 }),
buildDiffResult(baseB, curB, { passed: false, diffPercent: 0.04, pixelsDiff: 1200 }),
];
summariseDiffResults(results)
// { total: 2, passed: 1, failed: 1, failedIds: [baseB] }Gallery / album
import { buildAlbumEntry, buildAlbumManifest, sortAlbumEntries, filterByTags, groupAlbumEntries } from 'react-native-view-screenshot';
const entries = [
buildAlbumEntry(uri1, { label: 'Home', tags: ['ios', 'light'], timestamp: 1718450000 }),
buildAlbumEntry(uri2, { label: 'Profile', tags: ['ios', 'dark'], timestamp: 1718450060 }),
buildAlbumEntry(uri3, { label: 'Feed', tags: ['android'], timestamp: 1718449900 }),
];
const album = buildAlbumManifest(entries, { name: 'Sprint 42 Regression' });
sortAlbumEntries(entries, 'timestamp', 'desc')
filterByTags(entries, ['ios'])
groupAlbumEntries(entries, e => e.tags[0] ?? 'other')
// { ios: [...], android: [...] }Capture sequences & storyboards
import { buildCaptureSequence, buildGifConfig, buildStoryboard } from 'react-native-view-screenshot';
// Ordered capture steps
const sequence = buildCaptureSequence([
{ label: 'Splash', waitMs: 500 },
{ label: 'Onboard', waitMs: 300, scrollTo: { x: 0, y: 0 } },
{ label: 'Home', waitMs: 200, captureOpts: { format: 'jpg' } },
{ label: 'Settings', waitMs: 0 },
], { name: 'app-flow' });
// GIF config
buildGifConfig(uris, { frameDelayMs: 800, loop: true, width: 390, outputPath: '/tmp/demo.gif' })
// Storyboard grid
buildStoryboard(
frames.map((uri, i) => ({ uri, label: sequence.steps[i].label })),
{ title: 'App Flow', columns: 4, thumbWidth: 180 },
)Thumbnails
import { thumbnailFilename, buildThumbnailConfig } from 'react-native-view-screenshot';
thumbnailFilename('screenshot_home_1718450000.png')
// 'screenshot_home_1718450000_thumb.png'
thumbnailFilename('capture.png', { suffix: 'sm', format: 'jpg' })
// 'capture_sm.jpg'
buildThumbnailConfig(sourceUri, { maxWidth: 160, maxHeight: 320, format: 'jpg', quality: 0.6 })Reports
HTML report
import { buildHtmlReport } from 'react-native-view-screenshot';
const html = buildHtmlReport(
screenshots.map(s => ({ uri: s.uri, label: s.label, meta: s.meta })),
{ title: 'Sprint 42 Regression', columns: 4, thumbMaxWidth: 240 },
);
// Write to disk (Node.js)
import { writeFileSync } from 'fs';
writeFileSync('report.html', html);Markdown report
import { buildMarkdownReport, screenshotToMarkdown } from 'react-native-view-screenshot';
const md = buildMarkdownReport(screenshots, { title: 'QA Screenshots', imageWidth: 300 });
// Single image
screenshotToMarkdown(uri, { alt: 'Home screen', width: 390, caption: 'Light mode — iPhone 15 Pro' })
// <img src="..." width="390" alt="Home screen" />
//
// *Light mode — iPhone 15 Pro*Accessibility
import { buildAltText } from 'react-native-view-screenshot';
buildAltText({ screen: 'HomeScreen', platform: 'ios', colorScheme: 'dark' })
// 'Screenshot of HomeScreen on ios in dark mode'CommonJS
const {
buildScreenshotMeta, buildAnnotationSet, buildHighlight,
buildDiffConfig, buildHtmlReport, autoRedactSensitive,
} = require('react-native-view-screenshot');License
MIT
