
Barcode Capture Rn
- 33 installs
- 17 repo stars
- Updated August 4, 2026
- scandit/skills
barcode-capture-rn is an agent skill that integrates Scandit BarcodeCapture into React Native scan screens.
About
barcode-capture-rn helps solo builders embed Scandit’s BarcodeCapture SDK in a React Native app, starting from a `ScanScreen` stub and evolving toward production scanning. The readme shows license initialization on `DataCaptureContext`, shared instance wiring, and imports from `scandit-react-native-datacapture-core` and `scandit-react-native-datacapture-barcode`. It assumes a navigator-mounted screen and uses `useFocusEffect` so camera frame sources start and stop with screen visibility—critical for battery and permission UX on real devices. Eval notes reference additive features like custom feedback, viewfinder styling, location selection, scan intention, and `codeDuplicateFilter`, so the skill reads as an incremental integration guide rather than a one-shot paste. You need a valid Scandit license key and React Navigation in place; it does not replace store compliance, native build setup, or server-side validation of scanned codes.
- React Native function component baseline with hooks and `@react-navigation/native` focus handling
- Scandit v8 APIs: `DataCaptureContext`, `Camera`, `DataCaptureView`, `BarcodeCapture` pipeline
- Configurable symbologies via `BarcodeCaptureSettings` and `Symbology` enums
- Overlay and session types (`BarcodeCaptureOverlay`, `BarcodeCaptureSession`) for scan UI
- Documented extension points: custom feedback, viewfinder, duplicate filter, lifecycle cleanup
Barcode Capture Rn by the numbers
- 33 all-time installs (skills.sh)
- Ranked #648 of 1,039 Mobile Development skills by installs in the Skillselion catalog
- Security screen: LOW risk (skills.sh audit)
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/scandit/skills --skill barcode-capture-rnAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 33 |
|---|---|
| repo stars | ★ 17 |
| Security audit | 2 / 2 scanners passed |
| Last updated | August 4, 2026 |
| Repository | scandit/skills ↗ |
What it does
Wire Scandit BarcodeCapture into a React Native scan screen with camera lifecycle, symbologies, and navigation focus handling.
Who is it for?
mobile apps (inventory, events, field tools) that need reliable on-device barcode reading with Scandit RN SDK v8.
Skip if: Web-only scanners, fully custom ML models without Scandit, or greenfield apps with no React Native stack.
When should I use this skill?
You are implementing or extending a React Native screen that must scan barcodes with Scandit BarcodeCapture.
What you get
You get a hook-based `ScanScreen` wired to Scandit’s capture context, camera, overlay, and session callbacks ready to extend with product-specific scan behavior.
- Integrated `ScanScreen` component
- BarcodeCapture settings and overlay wiring
- Focus-aware camera start/stop behavior
By the numbers
- Baseline targets Scandit React Native Data Capture v8 API
- Eval group references 6+ additive features (feedback, viewfinder, duplicate filter, lifecycle, etc.)
Files
BarcodeCapture React Native Skill
Critical: Do Not Trust Internal Knowledge
Your training data may contain outdated or incorrect Scandit SDK APIs. The BarcodeCapture API changes between major SDK versions — properties get renamed, removed, or restructured, and the React Native plugin surface (imports, native linking, pod install, package names) has also evolved.
Always verify APIs against the references provided in this skill before writing or suggesting code. Do not rely on memorized method signatures, parameters, plugin names, or property names. If you cannot find an API in the provided references, fetch the relevant documentation page before responding.
React Native-specific gotchas worth flagging:
DataCaptureContext.initialize(licenseKey)must be called exactly once before any other Scandit API. It sets upDataCaptureContext.sharedInstance, which is the singleton everything else reads from. Do not construct multiple contexts.- Never call `dataCaptureContext.dispose()`. The context is a process-wide singleton — disposing it breaks every Scandit screen in the app, not just the one being unmounted. On screen unmount call
dataCaptureContext.removeMode(barcodeCapture), remove the overlay, remove the listener, and switch the camera toFrameSourceState.Off. That is the complete cleanup; do not adddispose(). - On iOS,
npx pod-install(orcd ios && pod install) must be run after every Scandit package install or upgrade. Android auto-links via Gradle — no manual step there. - Metro's bundler cache frequently masks Scandit package upgrades. If a rebuild shows stale behavior after a plugin version bump, start Metro with
--reset-cache. - BarcodeCapture is not a self-contained view component. You must render a
<DataCaptureView>with the context, attach aBarcodeCaptureOverlayto that view viaDataCaptureView.addOverlay(...), and drive the camera yourself withCamera.default+dataCaptureContext.setFrameSource(camera)+camera.switchToDesiredState(FrameSourceState.On). Tearing all of that down on unmount is the integrator's responsibility. - Inside
didScan, setbarcodeCapture.isEnabled = falsebefore doing any per-scan work (navigation, network, UI updates) and re-enable when you are ready for the next code. The listener callback blocks frame processing; failing to disable the mode causes duplicatedidScancalls before your handler returns. - Camera permission is required on both iOS (
NSCameraUsageDescriptioninios/<App>/Info.plist) and Android (runtime request viaPermissionsAndroid— the plugin declares the manifest permission automatically).
Intent Routing
Based on the user's request, load the appropriate reference file before responding:
- Integrating BarcodeCapture from scratch (e.g. "add BarcodeCapture to my app", "set up barcode scanning", "how do I use BarcodeCapture in React Native", "how do I add a viewfinder") → read
references/integration.mdand follow the instructions there. - Migrating or upgrading an existing BarcodeCapture integration (e.g. "upgrade from v6 to v7", "migrate my BarcodeCapture", "bump the Scandit packages to v8", "what changed between SDK versions") → read
references/migration.mdand follow the instructions there.
API Usage Policy
Only use APIs that are explicitly documented in the Scandit references below. Do not invent or guess method signatures, parameters, property names, or imports. If unsure whether an API exists or how it is called — or if a TypeScript / runtime error occurs — fetch the relevant reference page before responding. Do not tell the user to check the docs themselves. After answering, always include the relevant link so the user can explore further.
Never construct or guess documentation URLs. When you need a specific class or property's API page: 1. First check whether the page you already fetched (e.g. the Advanced Configurations page) contains a direct hyperlink to it — topic pages link directly to relevant API symbols. Always request links alongside content in your fetch prompt. 2. If no direct link was found, fetch the API index (see Full API reference in the table below), extract the actual link from it, and follow that.
URL structures vary across SDK versions and package paths (e.g. api/ui/ subdirectory) and guessing will lead to 404s.
Framework variant policy
React Native apps can be written with class components or function components. Examples in this skill use function components with hooks because they match the official React Native samples and the current React Native convention. Even if the target project still contains legacy class components elsewhere, write new BarcodeCapture code as function components — do not rewrite the rest of the app's component style, but keep the BarcodeCapture integration itself on the current idiom (useRef, useEffect, useFocusEffect, useMemo).
Examples are in TypeScript (.tsx). If the target project is plain JavaScript (.js / .jsx), drop the type annotations and keep the same imports and structure.
References
Direct users to the right resource based on their question:
| Topic | Resource |
|---|---|
| React Native integration | Get Started · Samples |
| Advanced topics (custom viewfinder, location selection, scan intention, composite codes, feedback) | Advanced Configurations |
| Migration between major SDK versions | 6 → 7 · 7 → 8 |
| Full API reference | BarcodeCapture API |
// Entry point component for the React Native app. The BarcodeCapture
// integration should go here as a function component with hooks.
// Assume this file is the screen rendered by the navigator / App root.
import React from 'react';
import { View, Text } from 'react-native';
export const ScanScreen = () => {
// TODO: integrate BarcodeCapture here.
return (
<View style={{ flex: 1 }}>
<Text>Scanner goes here</Text>
</View>
);
};
// Basic BarcodeCapture integration already in place (React Native, v8 API,
// function component with hooks). Evals in this group add features on top of
// this baseline (custom feedback, viewfinder, location selection, scan
// intention, codeDuplicateFilter, lifecycle cleanup, etc.).
import React, { useCallback, useEffect, useRef, useState } from 'react';
import { View, Text, StyleSheet } from 'react-native';
import { useFocusEffect } from '@react-navigation/native';
import {
Camera,
DataCaptureContext,
DataCaptureView,
FrameSourceState,
} from 'scandit-react-native-datacapture-core';
import {
BarcodeCapture,
BarcodeCaptureOverlay,
BarcodeCaptureSession,
BarcodeCaptureSettings,
Symbology,
SymbologyDescription,
} from 'scandit-react-native-datacapture-barcode';
const licenseKey = '-- ENTER YOUR SCANDIT LICENSE KEY HERE --';
DataCaptureContext.initialize(licenseKey);
const dataCaptureContext = DataCaptureContext.sharedInstance;
export const ScanScreen = () => {
const [lastScan, setLastScan] = useState<string | null>(null);
const viewRef = useRef<DataCaptureView | null>(null);
const cameraRef = useRef<Camera | null>(null);
const barcodeCaptureRef = useRef<BarcodeCapture | null>(null);
if (cameraRef.current === null) {
const camera = Camera.default;
camera?.applySettings(BarcodeCapture.recommendedCameraSettings);
dataCaptureContext.setFrameSource(camera);
cameraRef.current = camera;
}
if (barcodeCaptureRef.current === null) {
const settings = new BarcodeCaptureSettings();
settings.enableSymbologies([
Symbology.EAN13UPCA,
Symbology.Code128,
Symbology.QR,
]);
const barcodeCapture = new BarcodeCapture(settings);
dataCaptureContext.addMode(barcodeCapture);
barcodeCaptureRef.current = barcodeCapture;
}
useEffect(() => {
const barcodeCapture = barcodeCaptureRef.current!;
const listener = {
didScan: async (
mode: BarcodeCapture,
session: BarcodeCaptureSession,
) => {
const barcode = session.newlyRecognizedBarcode;
if (barcode == null) return;
mode.isEnabled = false;
const symbology = new SymbologyDescription(barcode.symbology);
setLastScan(`${barcode.data} (${symbology.readableName})`);
mode.isEnabled = true;
},
};
barcodeCapture.addListener(listener);
const overlay = new BarcodeCaptureOverlay(barcodeCapture);
viewRef.current?.addOverlay(overlay);
return () => {
// No teardown of the mode yet — evals may add removeMode here.
};
}, []);
useFocusEffect(
useCallback(() => {
const barcodeCapture = barcodeCaptureRef.current!;
const camera = cameraRef.current;
barcodeCapture.isEnabled = true;
camera?.switchToDesiredState(FrameSourceState.On);
return () => {
barcodeCapture.isEnabled = false;
camera?.switchToDesiredState(FrameSourceState.Off);
};
}, []),
);
return (
<View style={styles.container}>
<DataCaptureView
style={styles.preview}
context={dataCaptureContext}
ref={view => { viewRef.current = view; }}
/>
<View style={styles.results}>
<Text>{lastScan ?? 'Waiting for scan...'}</Text>
</View>
</View>
);
};
const styles = StyleSheet.create({
container: { flex: 1 },
preview: { flex: 1 },
results: { padding: 16, backgroundColor: '#fff' },
});
{
"name": "my-rn-barcode-capture-app",
"version": "1.0.0",
"description": "React Native app using BarcodeCapture v6",
"private": true,
"scripts": {
"start": "react-native start",
"android": "react-native run-android",
"ios": "react-native run-ios"
},
"dependencies": {
"react": "18.2.0",
"react-native": "0.72.0",
"scandit-react-native-datacapture-core": "6.28.1",
"scandit-react-native-datacapture-barcode": "6.28.1"
},
"devDependencies": {
"@babel/core": "^7.20.0",
"@types/react": "^18.0.0",
"typescript": "^4.9.0"
}
}
// Product list screen with basic React state management.
// BarcodeCapture needs to be added: scanned barcodes should be appended to
// scannedProducts via setScannedProducts and the list re-rendered.
import React, { useState, useCallback } from 'react';
import { View, Text, FlatList, Pressable, StyleSheet } from 'react-native';
export interface ScannedProduct {
data: string;
symbology: string;
}
export const ProductListScreen = () => {
const [scannedProducts, setScannedProducts] = useState<ScannedProduct[]>([]);
const onClearTapped = useCallback(() => {
setScannedProducts([]);
}, []);
return (
<View style={styles.container}>
<Text style={styles.total}>Total scanned: {scannedProducts.length}</Text>
<FlatList
style={styles.list}
data={scannedProducts}
keyExtractor={(_, i) => String(i)}
renderItem={({ item, index }) => (
<View style={styles.row}>
<Text>
{index + 1}. {item.data} ({item.symbology})
</Text>
</View>
)}
/>
<Pressable style={styles.clearButton} onPress={onClearTapped}>
<Text style={styles.clearText}>CLEAR LIST</Text>
</Pressable>
</View>
);
};
const styles = StyleSheet.create({
container: { flex: 1 },
total: { padding: 12, fontWeight: 'bold' },
list: { flex: 1 },
row: { paddingHorizontal: 12, paddingVertical: 8, borderBottomWidth: 0.5, borderColor: '#ccc' },
clearButton: {
alignItems: 'center', padding: 12, margin: 16,
borderWidth: 2, borderColor: '#000', borderRadius: 4,
},
clearText: { fontWeight: 'bold' },
});
// Pre-migration v6 BarcodeCapture React Native integration.
// Uses v6 API surface: DataCaptureContext.forLicenseKey,
// BarcodeCapture.forContext, BarcodeCaptureOverlay.withBarcodeCaptureForView,
// and dataCaptureContext.dispose() teardown.
//
// The skill should migrate this to v7 or v8.
import React, { useEffect, useMemo, useRef, useState } from 'react';
import { View } from 'react-native';
import {
Camera,
DataCaptureContext,
DataCaptureView,
FrameSourceState,
} from 'scandit-react-native-datacapture-core';
import {
BarcodeCapture,
BarcodeCaptureOverlay,
BarcodeCaptureSession,
BarcodeCaptureSettings,
Symbology,
} from 'scandit-react-native-datacapture-barcode';
export const ScanScreen = () => {
const [, setLastScan] = useState<string | null>(null);
const dataCaptureContext = useMemo(
() => DataCaptureContext.forLicenseKey('YOUR_LICENSE_KEY'),
[],
);
const viewRef = useRef<DataCaptureView | null>(null);
const barcodeCaptureRef = useRef<BarcodeCapture | null>(null);
const cameraRef = useRef<Camera | null>(null);
if (cameraRef.current === null) {
const camera = Camera.default;
camera?.applySettings(BarcodeCapture.recommendedCameraSettings);
dataCaptureContext.setFrameSource(camera);
cameraRef.current = camera;
}
if (barcodeCaptureRef.current === null) {
const settings = new BarcodeCaptureSettings();
settings.enableSymbologies([
Symbology.EAN13UPCA,
Symbology.Code128,
Symbology.QR,
]);
// v6 factory — deprecated in v8.
barcodeCaptureRef.current = BarcodeCapture.forContext(
dataCaptureContext,
settings,
);
barcodeCaptureRef.current.addListener({
didScan: async (
mode: BarcodeCapture,
session: BarcodeCaptureSession,
) => {
const barcode = session.newlyRecognizedBarcode;
if (barcode) {
mode.isEnabled = false;
setLastScan(barcode.data ?? null);
mode.isEnabled = true;
}
},
});
}
useEffect(() => {
// v6 overlay factory — still works but the v8 idiom is `new BarcodeCaptureOverlay(...)`.
const overlay = BarcodeCaptureOverlay.withBarcodeCaptureForView(
barcodeCaptureRef.current!,
viewRef.current,
);
Camera.default?.switchToDesiredState(FrameSourceState.On);
return () => {
// v6/v7 teardown pattern — in v8 this becomes removeMode(barcodeCaptureRef.current).
Camera.default?.switchToDesiredState(FrameSourceState.Off);
// @ts-expect-error v7 only — not present in v8 singleton context
dataCaptureContext.dispose();
void overlay;
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return (
<View style={{ flex: 1 }}>
<DataCaptureView
style={{ flex: 1 }}
context={dataCaptureContext}
ref={view => { viewRef.current = view; }}
/>
</View>
);
};
// Pre-migration v7 BarcodeCapture React Native integration.
// Uses v7 API surface: DataCaptureContext.forLicenseKey (still valid in v7),
// BarcodeCapture.forContext (still valid in v7), and
// BarcodeCaptureOverlay.withBarcodeCaptureForView.
//
// The skill should migrate this to v8.
import React, { useEffect, useMemo, useRef, useState } from 'react';
import { View } from 'react-native';
import {
Camera,
DataCaptureContext,
DataCaptureView,
FrameSourceState,
} from 'scandit-react-native-datacapture-core';
import {
BarcodeCapture,
BarcodeCaptureOverlay,
BarcodeCaptureSession,
BarcodeCaptureSettings,
Symbology,
} from 'scandit-react-native-datacapture-barcode';
export const ScanScreen = () => {
const [, setLastScan] = useState<string | null>(null);
const dataCaptureContext = useMemo(
() => DataCaptureContext.forLicenseKey('YOUR_LICENSE_KEY'),
[],
);
const viewRef = useRef<DataCaptureView | null>(null);
const barcodeCaptureRef = useRef<BarcodeCapture | null>(null);
const cameraRef = useRef<Camera | null>(null);
if (cameraRef.current === null) {
const camera = Camera.default;
camera?.applySettings(BarcodeCapture.recommendedCameraSettings);
dataCaptureContext.setFrameSource(camera);
cameraRef.current = camera;
}
if (barcodeCaptureRef.current === null) {
const settings = new BarcodeCaptureSettings();
settings.enableSymbologies([
Symbology.EAN13UPCA,
Symbology.Code128,
Symbology.QR,
]);
// v7 factory — still valid in v7, deprecated in v8.
barcodeCaptureRef.current = BarcodeCapture.forContext(
dataCaptureContext,
settings,
);
barcodeCaptureRef.current.addListener({
didScan: async (
mode: BarcodeCapture,
session: BarcodeCaptureSession,
) => {
const barcode = session.newlyRecognizedBarcode;
if (barcode) {
mode.isEnabled = false;
setLastScan(barcode.data ?? null);
mode.isEnabled = true;
}
},
});
}
useEffect(() => {
// v7 overlay factory.
const overlay = BarcodeCaptureOverlay.withBarcodeCaptureForView(
barcodeCaptureRef.current!,
viewRef.current,
);
Camera.default?.switchToDesiredState(FrameSourceState.On);
return () => {
Camera.default?.switchToDesiredState(FrameSourceState.Off);
// v7 teardown — in v8 the context is a singleton; replace with removeMode.
dataCaptureContext.dispose();
void overlay;
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return (
<View style={{ flex: 1 }}>
<DataCaptureView
style={{ flex: 1 }}
context={dataCaptureContext}
ref={view => { viewRef.current = view; }}
/>
</View>
);
};
// Existing barcode scanner built on react-native-vision-camera's built-in
// code scanner (useCodeScanner). The app collects scanned values, deduplicates
// them, and shows a running summary. We want to migrate this to Scandit
// BarcodeCapture while keeping the same dedup + summary behavior.
import React, { useState, useCallback } from 'react';
import { View, Text, FlatList, StyleSheet } from 'react-native';
import {
Camera,
useCameraDevice,
useCameraPermission,
useCodeScanner,
Code,
} from 'react-native-vision-camera';
interface ScannedCode {
value: string;
type: string;
}
export const VisionCameraScanner = () => {
const device = useCameraDevice('back');
const { hasPermission, requestPermission } = useCameraPermission();
const [scanned, setScanned] = useState<ScannedCode[]>([]);
const codeScanner = useCodeScanner({
codeTypes: ['ean-13', 'code-128', 'qr'],
onCodeScanned: (codes: Code[]) => {
for (const code of codes) {
const value = code.value;
if (value == null) continue;
setScanned(prev => {
// Deduplicate: ignore a value we've already collected.
if (prev.some(c => c.value === value)) return prev;
return [...prev, { value, type: code.type }];
});
}
},
});
const onStart = useCallback(async () => {
if (!hasPermission) await requestPermission();
}, [hasPermission, requestPermission]);
if (device == null) {
return (
<View style={styles.center}>
<Text>No camera device</Text>
</View>
);
}
return (
<View style={styles.container}>
<Camera
style={StyleSheet.absoluteFill}
device={device}
isActive={true}
codeScanner={codeScanner}
onTouchStart={onStart}
/>
<View style={styles.summary}>
<Text style={styles.title}>Scanned: {scanned.length}</Text>
<FlatList
data={scanned}
keyExtractor={item => item.value}
renderItem={({ item }) => (
<Text style={styles.row}>
{item.type}: {item.value}
</Text>
)}
/>
</View>
</View>
);
};
const styles = StyleSheet.create({
container: { flex: 1 },
center: { flex: 1, alignItems: 'center', justifyContent: 'center' },
summary: { position: 'absolute', bottom: 0, left: 0, right: 0, padding: 16, backgroundColor: '#fff' },
title: { fontWeight: 'bold', marginBottom: 8 },
row: { paddingVertical: 4 },
});
{
"skill_name": "barcode-capture-rn",
"evals": [
{
"id": 1,
"prompt": "I want to add BarcodeCapture to my React Native app. Here's my screen file: EmptyApp.tsx. I need to scan EAN-13 and Code 128 barcodes for a retail app.",
"expected_output": "The skill reads integration.md, writes complete BarcodeCapture integration code into EmptyApp.tsx as a function component with hooks (DataCaptureView + BarcodeCaptureOverlay + Camera), and shows the setup checklist (packages to install, npx pod-install, camera permission entries).",
"files": [
"fixtures/EmptyApp.tsx"
],
"assertions": [
{"text": "Setup checklist is shown (mentions scandit-react-native-datacapture-core and scandit-react-native-datacapture-barcode)"},
{"text": "Setup checklist mentions npx pod-install for iOS"},
{"text": "Setup checklist mentions NSCameraUsageDescription in ios Info.plist"},
{"text": "Setup checklist mentions Android runtime camera permission (PermissionsAndroid) or notes Android auto-links"},
{"text": "DataCaptureContext.initialize( is present (v8 API used)"},
{"text": "DataCaptureContext.sharedInstance is used to obtain the context"},
{"text": "DataCaptureContext.forLicenseKey( is NOT present"},
{"text": "A license key placeholder string is present (semantic)"},
{"text": "new BarcodeCapture(settings) is present (v8 constructor used, not BarcodeCapture.forContext)"},
{"text": "dataCaptureContext.addMode( is called to attach the BarcodeCapture mode to the context"},
{"text": "Symbology.EAN13UPCA is enabled"},
{"text": "Symbology.Code128 is enabled"},
{"text": "No symbologies except EAN13UPCA and Code128 are enabled"},
{"text": "<DataCaptureView is rendered as JSX with a context prop"},
{"text": "BarcodeCaptureOverlay is constructed and added to the DataCaptureView via addOverlay or withBarcodeCaptureForView (semantic)"},
{"text": "Camera.default is present"},
{"text": "dataCaptureContext.setFrameSource(camera) is called"},
{"text": "camera.switchToDesiredState(FrameSourceState.On) is called to start the camera"},
{"text": "The component is written as a function component (no extends React.Component / class syntax)"},
{"text": "useRef is used to hold the BarcodeCapture mode reference across renders"},
{"text": "addListener is called on the BarcodeCapture mode to register a listener with a didScan callback"},
{"text": "The didScan callback sets barcodeCapture.isEnabled = false (or mode.isEnabled = false) before processing the scan"},
{"text": "Imports come from scandit-react-native-datacapture-core (DataCaptureContext, DataCaptureView, Camera, FrameSourceState) and scandit-react-native-datacapture-barcode (BarcodeCapture, BarcodeCaptureSettings, BarcodeCaptureOverlay, Symbology)"}
]
},
{
"id": 2,
"prompt": "Add BarcodeCapture barcode scanning to ProductListApp.tsx. When a barcode is scanned, append it to the scannedProducts state and re-render the product list. Keep the existing clear button and list rendering.",
"expected_output": "The skill adds BarcodeCapture to ProductListApp.tsx. The existing scannedProducts state, render logic, and clear button are preserved. Scanned barcodes are appended via setScannedProducts.",
"files": [
"fixtures/ProductListApp.tsx"
],
"assertions": [
{"text": "DataCaptureContext.initialize( is present (v8 API)"},
{"text": "DataCaptureContext.forLicenseKey( is NOT present"},
{"text": "new BarcodeCapture(settings) is present"},
{"text": "dataCaptureContext.addMode( is called"},
{"text": "<DataCaptureView is rendered in the component's JSX"},
{"text": "BarcodeCaptureOverlay is created and added to the DataCaptureView"},
{"text": "barcodeCapture.addListener( is called with a didScan callback"},
{"text": "didScan callback pushes / appends to scannedProducts via setScannedProducts (or equivalent state setter)"},
{"text": "Existing scannedProducts state and setScannedProducts setter are preserved"},
{"text": "Existing list rendering (FlatList / ScrollView / map) is preserved"},
{"text": "Existing clear button / onClearTapped handler is preserved"},
{"text": "useEffect cleanup calls dataCaptureContext.removeMode(...)"}
]
},
{
"id": 3,
"prompt": "I want to draw a square viewfinder over the camera preview in my BarcodeCapture React Native app so users know where to aim.",
"expected_output": "The skill creates a RectangularViewfinder and assigns it to the BarcodeCaptureOverlay's viewfinder property.",
"files": [
"fixtures/IntegratedApp.tsx"
],
"assertions": [
{"text": "new RectangularViewfinder( is constructed"},
{"text": "RectangularViewfinder is imported from scandit-react-native-datacapture-core"},
{"text": "The viewfinder is assigned to the BarcodeCaptureOverlay (overlay.viewfinder = ...)"},
{"text": "Existing integration code (context, BarcodeCapture mode, DataCaptureView JSX, listener, overlay) is preserved"}
]
},
{
"id": 4,
"prompt": "Customize the success feedback in my BarcodeCapture React Native app so it vibrates but does NOT play a sound on each scan.",
"expected_output": "The skill assigns a Feedback to barcodeCapture.feedback.success with a default vibration and a null sound, using BarcodeCaptureFeedback.defaultFeedback as the starting point.",
"files": [
"fixtures/IntegratedApp.tsx"
],
"assertions": [
{"text": "BarcodeCaptureFeedback.defaultFeedback (or new BarcodeCaptureFeedback()) is used as the starting feedback"},
{"text": "feedback.success is assigned a new Feedback instance"},
{"text": "Vibration.defaultVibration is passed to the Feedback constructor"},
{"text": "null is passed for the sound argument of Feedback (no sound)"},
{"text": "barcodeCapture.feedback is set to the customized feedback"},
{"text": "Imports for Feedback and Vibration are added from scandit-react-native-datacapture-core"},
{"text": "Imports for BarcodeCaptureFeedback are added from scandit-react-native-datacapture-barcode"},
{"text": "Existing integration code (context, BarcodeCapture mode, DataCaptureView JSX, listener, overlay) is preserved"}
]
},
{
"id": 5,
"prompt": "I only want to accept barcodes that appear inside a circular region in the middle of the frame (50% of the smaller dimension). Restrict the scan area in my BarcodeCapture React Native app.",
"expected_output": "The skill sets settings.locationSelection to a RadiusLocationSelection with a 0.5 fraction radius and applies the settings to BarcodeCapture.",
"files": [
"fixtures/IntegratedApp.tsx"
],
"assertions": [
{"text": "new RadiusLocationSelection( is constructed"},
{"text": "NumberWithUnit and MeasureUnit.Fraction are used to express the radius"},
{"text": "settings.locationSelection is assigned the RadiusLocationSelection"},
{"text": "Settings are applied to the BarcodeCapture mode either via barcodeCapture.applySettings(settings) or by passing settings into the constructor before the mode is added to the context (semantic)"},
{"text": "RadiusLocationSelection, NumberWithUnit, MeasureUnit are imported from scandit-react-native-datacapture-core"},
{"text": "Existing integration code (context, BarcodeCapture mode, DataCaptureView JSX, listener, overlay) is preserved"}
]
},
{
"id": 6,
"prompt": "I want to filter scans so duplicates within 500 milliseconds are ignored. Update my BarcodeCapture settings.",
"expected_output": "The skill sets codeDuplicateFilter on the BarcodeCaptureSettings instance before constructing the BarcodeCapture mode (or applies updated settings).",
"files": [
"fixtures/IntegratedApp.tsx"
],
"assertions": [
{"text": "settings.codeDuplicateFilter = 500 is set (or equivalent in the API units)"},
{"text": "codeDuplicateFilter is set before constructing BarcodeCapture, or barcodeCapture.applySettings(settings) is called after the change (semantic)"},
{"text": "Existing integration code (context, BarcodeCapture mode, DataCaptureView JSX, listener, overlay) is preserved"}
]
},
{
"id": 7,
"prompt": "Switch my BarcodeCapture React Native app to manual scan intention so the SDK does not try to guess which barcode the user wants.",
"expected_output": "The skill sets settings.scanIntention = ScanIntention.Manual and applies the settings.",
"files": [
"fixtures/IntegratedApp.tsx"
],
"assertions": [
{"text": "settings.scanIntention = ScanIntention.Manual is set"},
{"text": "ScanIntention is imported from scandit-react-native-datacapture-core"},
{"text": "The updated settings (including scanIntention) are applied to the BarcodeCapture mode, either by calling barcodeCapture.applySettings(settings) or by passing the configured settings into the BarcodeCapture constructor (semantic)"},
{"text": "Existing integration code (context, BarcodeCapture mode, DataCaptureView JSX, listener, overlay) is preserved"}
]
},
{
"id": 8,
"prompt": "I need to clean up properly when my React Native scan screen unmounts — stop the camera, remove the overlay, and unbind the BarcodeCapture mode from the context so its native resources are released.",
"expected_output": "The skill wires the cleanup function in useEffect to call switchToDesiredState(FrameSourceState.Off), removeOverlay, removeListener, and dataCaptureContext.removeMode(barcodeCaptureRef.current).",
"files": [
"fixtures/IntegratedApp.tsx"
],
"assertions": [
{"text": "useEffect cleanup (returned function from useEffect) calls dataCaptureContext.removeMode(...)"},
{"text": "The BarcodeCapture mode passed to removeMode is the one held in the useRef"},
{"text": "camera.switchToDesiredState(FrameSourceState.Off) (or useFocusEffect cleanup) stops the camera"},
{"text": "dataCaptureView.removeOverlay(...) is called for the overlay (in the cleanup or via the useEffect return)"},
{"text": "dataCaptureContext.dispose() is never actually invoked in the cleanup logic — it may appear in a comment explaining the rule, but no executable call to dispose() exists (semantic)"},
{"text": "Existing integration code (context, BarcodeCapture mode, DataCaptureView JSX, listener, overlay) is preserved"}
]
},
{
"id": 9,
"prompt": "Change the symbologies my BarcodeCapture React Native app accepts to only QR and DataMatrix.",
"expected_output": "The skill replaces the enabled symbologies with Symbology.QR and Symbology.DataMatrix and re-applies the settings.",
"files": [
"fixtures/IntegratedApp.tsx"
],
"assertions": [
{"text": "Symbology.QR is enabled"},
{"text": "Symbology.DataMatrix is enabled"},
{"text": "The active enabled symbology list no longer includes Symbology.EAN13UPCA (it may still appear in a removed/before comment, but is not in the call to enableSymbologies) (semantic)"},
{"text": "The active enabled symbology list no longer includes Symbology.Code128 (it may still appear in a removed/before comment, but is not in the call to enableSymbologies) (semantic)"},
{"text": "settings.enableSymbologies(...) is called with QR and DataMatrix"},
{"text": "Existing integration code (context, BarcodeCapture mode, DataCaptureView JSX, listener, overlay) is preserved"}
]
},
{
"id": 10,
"prompt": "I want to enable composite codes (CompositeType A) alongside the regular barcodes in my BarcodeCapture React Native app.",
"expected_output": "The skill sets settings.enabledCompositeTypes to include CompositeType.A and calls settings.enableSymbologiesForCompositeTypes(...) to enable the symbologies the composite type needs.",
"files": [
"fixtures/IntegratedApp.tsx"
],
"assertions": [
{"text": "settings.enabledCompositeTypes is assigned an array containing CompositeType.A"},
{"text": "settings.enableSymbologiesForCompositeTypes(...) is called with the composite types"},
{"text": "CompositeType is imported from scandit-react-native-datacapture-barcode"},
{"text": "Existing integration code (context, BarcodeCapture mode, DataCaptureView JSX, listener, overlay) is preserved"}
]
},
{
"id": 11,
"prompt": "Add an aimer-style viewfinder (a crosshair dot with a frame) to my BarcodeCapture React Native app so users can aim at a single barcode. Make the dot red.",
"expected_output": "The skill constructs an AimerViewfinder, sets its dotColor to red, and assigns it to the BarcodeCaptureOverlay's viewfinder property.",
"files": [
"fixtures/IntegratedApp.tsx"
],
"assertions": [
{"text": "new AimerViewfinder( is constructed"},
{"text": "AimerViewfinder is imported from scandit-react-native-datacapture-core"},
{"text": "aimer.dotColor is assigned a Color value"},
{"text": "The aimer is assigned to the overlay via overlay.viewfinder ="},
{"text": "Existing integration code (context, BarcodeCapture mode, DataCaptureView JSX, listener, overlay) is preserved"}
],
"tags": ["aimer-viewfinder"]
},
{
"id": 12,
"prompt": "Add a laserline viewfinder to my BarcodeCapture React Native app for scanning 1D barcodes like a laser scanner.",
"expected_output": "The skill constructs a LaserlineViewfinder (no constructor arguments) and assigns it to the BarcodeCaptureOverlay's viewfinder property.",
"files": [
"fixtures/IntegratedApp.tsx"
],
"assertions": [
{"text": "new LaserlineViewfinder( is constructed"},
{"text": "LaserlineViewfinder is imported from scandit-react-native-datacapture-core"},
{"text": "The laserline viewfinder is assigned to the overlay via overlay.viewfinder ="},
{"text": "Existing integration code (context, BarcodeCapture mode, DataCaptureView JSX, listener, overlay) is preserved"}
],
"tags": ["laserline-viewfinder"]
},
{
"id": 13,
"prompt": "In my BarcodeCapture React Native app I only want to accept barcodes whose data starts with 'SKU-'. Any other barcode should be ignored and not highlighted.",
"expected_output": "The skill rejects non-matching codes inside didScan by checking barcode.data, setting overlay.brush = Brush.transparent and returning early without processing; accepted codes proceed normally.",
"files": [
"fixtures/IntegratedApp.tsx"
],
"assertions": [
{"text": "Inside didScan, barcode.data?.startsWith('SKU-') (or barcode.data.startsWith) is used to decide acceptance"},
{"text": "For a rejected barcode, overlay.brush = Brush.transparent is set"},
{"text": "For a rejected barcode the callback returns early without processing it (a return statement after the rejection branch) (semantic)"},
{"text": "Brush is imported from scandit-react-native-datacapture-core"},
{"text": "Existing integration code (context, BarcodeCapture mode, DataCaptureView JSX, listener, overlay) is preserved"}
],
"tags": ["reject-pattern"]
},
{
"id": 14,
"prompt": "Change the highlight color of recognized barcodes in my BarcodeCapture React Native app to a semi-transparent green fill with a solid green outline.",
"expected_output": "The skill assigns overlay.brush a new Brush built from a semi-transparent green fill Color, a green stroke Color, and a stroke width.",
"files": [
"fixtures/IntegratedApp.tsx"
],
"assertions": [
{"text": "overlay.brush is assigned a new Brush( instance"},
{"text": "The Brush constructor receives a fill Color, a stroke Color, and a numeric stroke width"},
{"text": "Color.fromRGBA or Color.fromHex is used to build the brush colors"},
{"text": "Brush and Color are imported from scandit-react-native-datacapture-core"},
{"text": "Existing integration code (context, BarcodeCapture mode, DataCaptureView JSX, listener, overlay) is preserved"}
],
"tags": ["overlay-brush"]
},
{
"id": 15,
"prompt": "My Code 39 barcodes contain the full ASCII character set. Enable the full ASCII extension for Code 39 in my BarcodeCapture React Native app.",
"expected_output": "The skill gets the per-symbology settings via settings.settingsForSymbology(Symbology.Code39) and calls setExtensionEnabled('full_ascii', true), then applies the settings.",
"files": [
"fixtures/IntegratedApp.tsx"
],
"assertions": [
{"text": "settings.settingsForSymbology(Symbology.Code39) is called to get the per-symbology settings"},
{"text": "setExtensionEnabled('full_ascii', true) is called on the Code39 symbology settings"},
{"text": "The settings are applied to the mode via barcodeCapture.applySettings(settings) or passed into the constructor before the mode is added (semantic)"},
{"text": "Existing integration code (context, BarcodeCapture mode, DataCaptureView JSX, listener, overlay) is preserved"}
],
"tags": ["symbology-extensions"]
},
{
"id": 16,
"prompt": "Require a Mod 43 checksum for Code 39 barcodes in my BarcodeCapture React Native app so misreads are rejected.",
"expected_output": "The skill gets settings.settingsForSymbology(Symbology.Code39) and sets its checksums to [Checksum.Mod43], then applies the settings.",
"files": [
"fixtures/IntegratedApp.tsx"
],
"assertions": [
{"text": "settings.settingsForSymbology(Symbology.Code39) is called"},
{"text": "The checksums property of the Code39 symbology settings is assigned [Checksum.Mod43]"},
{"text": "Checksum is imported from scandit-react-native-datacapture-barcode"},
{"text": "The settings are applied to the mode via barcodeCapture.applySettings(settings) or passed into the constructor before the mode is added (semantic)"},
{"text": "Existing integration code (context, BarcodeCapture mode, DataCaptureView JSX, listener, overlay) is preserved"}
],
"tags": ["symbology-checksums"]
},
{
"id": 17,
"prompt": "My Code 39 labels are a fixed length and I want to restrict scanning to codes between 7 and 20 symbols long in my BarcodeCapture React Native app.",
"expected_output": "The skill gets settings.settingsForSymbology(Symbology.Code39) and assigns its activeSymbolCounts an array covering 7 through 20, then applies the settings.",
"files": [
"fixtures/IntegratedApp.tsx"
],
"assertions": [
{"text": "settings.settingsForSymbology(Symbology.Code39) is called"},
{"text": "The activeSymbolCounts property of the Code39 symbology settings is assigned an array of numbers"},
{"text": "The activeSymbolCounts array spans the requested 7 to 20 range (semantic)"},
{"text": "The settings are applied to the mode via barcodeCapture.applySettings(settings) or passed into the constructor before the mode is added (semantic)"},
{"text": "Existing integration code (context, BarcodeCapture mode, DataCaptureView JSX, listener, overlay) is preserved"}
],
"tags": ["active-symbol-counts"]
},
{
"id": 18,
"prompt": "Some of my Code 128 barcodes are printed bright-on-dark (color inverted). Enable color-inverted decoding for Code 128 in my BarcodeCapture React Native app.",
"expected_output": "The skill gets settings.settingsForSymbology(Symbology.Code128) and sets isColorInvertedEnabled = true, then applies the settings.",
"files": [
"fixtures/IntegratedApp.tsx"
],
"assertions": [
{"text": "settings.settingsForSymbology(Symbology.Code128) is called"},
{"text": "isColorInvertedEnabled = true is set on the Code128 symbology settings"},
{"text": "The settings are applied to the mode via barcodeCapture.applySettings(settings) or passed into the constructor before the mode is added (semantic)"},
{"text": "Existing integration code (context, BarcodeCapture mode, DataCaptureView JSX, listener, overlay) is preserved"}
],
"tags": ["color-inverted"]
}
]
}
{
"skill_name": "barcode-capture-rn",
"evals": [
{
"id": 1,
"prompt": "I'm on Scandit SDK v6 and need to migrate my BarcodeCapture React Native integration to v7. Here's my scanner file: ScannerApp_v6.tsx",
"expected_output": "The skill reads migration.md, applies the 6→7 changes (mostly informational — scan intention default, codeDuplicateFilter default, BarcodeTracking→BarcodeBatch rename if applicable) to ScannerApp_v6.tsx, and summarizes all changes made.",
"files": [
"fixtures/ScannerApp_v6.tsx"
],
"assertions": [
{"text": "Summary mentions the v7 default scanIntention change to Smart"},
{"text": "Summary mentions the codeDuplicateFilter -2 default behavior change in v7.1+"},
{"text": "BarcodeCapture.forContext( is still allowed (not removed in v7)"},
{"text": "BarcodeCaptureOverlay.withBarcodeCaptureForView is still allowed (not removed in v7)"},
{"text": "DataCaptureContext.forLicenseKey is still allowed (not removed in v7)"},
{"text": "A summary is shown describing changes"}
]
},
{
"id": 2,
"prompt": "We're upgrading our React Native app from Scandit SDK 7 to 8. Here's our BarcodeCapture code: ScannerApp_v7.tsx. Please apply any necessary changes.",
"expected_output": "The skill applies the v7→v8 renames (DataCaptureContext.forLicenseKey → DataCaptureContext.initialize + sharedInstance, BarcodeCapture.forContext → new BarcodeCapture + addMode, BarcodeCaptureOverlay.withBarcodeCaptureForView → new BarcodeCaptureOverlay + addOverlay, dataCaptureContext.dispose() → dataCaptureContext.removeMode(barcodeCapture)) and summarizes the changes.",
"files": [
"fixtures/ScannerApp_v7.tsx"
],
"assertions": [
{"text": "DataCaptureContext.initialize( is present in the output file"},
{"text": "DataCaptureContext.sharedInstance is used to read the context"},
{"text": "DataCaptureContext.forLicenseKey( is NOT present in the output file"},
{"text": "new BarcodeCapture( is present in the output file"},
{"text": "BarcodeCapture.forContext( is NOT present in the output file"},
{"text": "dataCaptureContext.addMode( is called to attach the BarcodeCapture mode"},
{"text": "new BarcodeCaptureOverlay( is present (or BarcodeCaptureOverlay.withBarcodeCaptureForView replaced)"},
{"text": "dataCaptureView.addOverlay( is called for the BarcodeCaptureOverlay"},
{"text": "dataCaptureContext.dispose() is NOT present (replaced with removeMode or removed entirely)"},
{"text": "dataCaptureContext.removeMode( is present in the cleanup path"},
{"text": "A summary is shown listing the renames"},
{"text": "Summary mentions npx pod-install must be run after the package bump"},
{"text": "The output file is functionally equivalent to the input otherwise — no unnecessary changes"}
]
},
{
"id": 3,
"prompt": "We need to upgrade our BarcodeCapture integration from SDK 6 all the way to v8. Here's our current code: ScannerApp_v6.tsx",
"expected_output": "The skill applies both migrations in order (6→7 then 7→8) and summarizes all changes organized by migration step.",
"files": [
"fixtures/ScannerApp_v6.tsx"
],
"assertions": [
{"text": "DataCaptureContext.initialize( is present (7→8 rename applied)"},
{"text": "DataCaptureContext.forLicenseKey( is NOT present"},
{"text": "DataCaptureContext.sharedInstance is used to read the context"},
{"text": "new BarcodeCapture( is present (7→8 rename applied)"},
{"text": "BarcodeCapture.forContext( is NOT present"},
{"text": "dataCaptureContext.addMode( is called"},
{"text": "new BarcodeCaptureOverlay( is present"},
{"text": "dataCaptureContext.dispose() is NOT present"},
{"text": "dataCaptureContext.removeMode( is present in the cleanup"},
{"text": "Summary covers both migration steps (references both 6→7 and 7→8)"}
]
},
{
"id": 4,
"prompt": "I want to upgrade my BarcodeCapture to the latest Scandit SDK version. Here's my code and package.json.",
"expected_output": "The skill reads package.json to detect v6.28.x, determines the target is v8, applies both migrations without asking the user for their version.",
"files": [
"fixtures/ScannerApp_v6.tsx",
"fixtures/package_v6.json"
],
"assertions": [
{"text": "Summary mentions the detected version (6.28.x or v6)"},
{"text": "Summary mentions the package.json dependency versions need to be bumped to ^8.0.0"},
{"text": "Summary mentions npx pod-install must be run after the bump"},
{"text": "DataCaptureContext.initialize( is present (7→8 rename applied)"},
{"text": "new BarcodeCapture( is present (7→8 rename applied)"},
{"text": "dataCaptureContext.addMode( is called"},
{"text": "dataCaptureContext.dispose() is NOT present"},
{"text": "Did not ask the user for their version — proceeded autonomously"}
]
},
{
"id": 5,
"prompt": "Migrate my BarcodeCapture overlay creation in ScannerApp_v7.tsx to the v8 idiom — I want the explicit `new BarcodeCaptureOverlay(...)` + `dataCaptureView.addOverlay(...)` form, not the legacy factory.",
"expected_output": "The skill replaces BarcodeCaptureOverlay.withBarcodeCaptureForView with new BarcodeCaptureOverlay(barcodeCapture) and adds dataCaptureView.addOverlay(overlay), and otherwise leaves the file untouched (the v7 surface is otherwise valid).",
"files": [
"fixtures/ScannerApp_v7.tsx"
],
"assertions": [
{"text": "new BarcodeCaptureOverlay( is present in the output file"},
{"text": "BarcodeCaptureOverlay.withBarcodeCaptureForView is NOT present"},
{"text": "dataCaptureView.addOverlay( or viewRef.current.addOverlay is called for the overlay"},
{"text": "A summary is shown listing the overlay rewrite"}
]
}
]
}
{
"skill_name": "barcode-capture-rn",
"evals": [
{
"id": 1,
"prompt": "I have a React Native app that scans barcodes with react-native-vision-camera's built-in code scanner (useCodeScanner). I want to replace it with Scandit BarcodeCapture. Here is my screen: VisionCameraScanner.tsx",
"expected_output": "The skill removes the react-native-vision-camera code scanner (useCodeScanner, the Camera codeScanner prop, vision-camera imports), adds a Scandit BarcodeCapture integration with the matching symbologies (EAN13UPCA, Code128, QR), preserves the ScannedCode dedup logic and the scanned-count summary, and shows a setup checklist.",
"files": [
"fixtures/VisionCameraScanner.tsx"
],
"assertions": [
{"text": "react-native-vision-camera is NOT imported in the output code (it may appear in a before/after summary)"},
{"text": "useCodeScanner is NOT called in the output code (it may appear in a before/after summary)"},
{"text": "The vision-camera Camera component's codeScanner prop is NOT present in the output code (it may appear in a before/after summary)"},
{"text": "DataCaptureContext.initialize( is present (v8 API)"},
{"text": "DataCaptureContext.sharedInstance is used to obtain the context"},
{"text": "new BarcodeCapture(settings) is present"},
{"text": "dataCaptureContext.addMode( is called to attach the BarcodeCapture mode"},
{"text": "settings.enableSymbologies(...) is called"},
{"text": "Symbology.EAN13UPCA is enabled (mapped from 'ean-13')"},
{"text": "Symbology.Code128 is enabled (mapped from 'code-128')"},
{"text": "Symbology.QR is enabled (mapped from 'qr')"},
{"text": "Symbology.QRCode does NOT appear as a Symbology enum value (the correct case is Symbology.QR)"},
{"text": "<DataCaptureView is rendered as JSX with a context prop"},
{"text": "new BarcodeCaptureOverlay( is constructed and added via addOverlay"},
{"text": "barcodeCapture.addListener( is called with a didScan callback"},
{"text": "The didScan callback reads session.newlyRecognizedBarcode"},
{"text": "camera.switchToDesiredState(FrameSourceState.On) is called to start the camera"},
{"text": "The existing dedup behavior is preserved (a scanned value already in the list is not added again) (semantic)"},
{"text": "The scanned-count summary (Scanned: <count>) is preserved in the rendered output"},
{"text": "Setup checklist mentions scandit-react-native-datacapture-core and scandit-react-native-datacapture-barcode"},
{"text": "Setup checklist mentions npx pod-install for iOS"},
{"text": "Setup checklist mentions NSCameraUsageDescription in the iOS Info.plist"},
{"text": "A summary of changes is shown listing what vision-camera code was removed and what Scandit code was added"}
],
"tags": ["third-party-migration"]
}
]
}
BarcodeCapture React Native Integration Guide
BarcodeCapture is the single-scan capture mode in the Scandit Data Capture SDK. Unlike SparkScan, it does not ship a pre-built UI — you wire up a DataCaptureView, attach a BarcodeCaptureOverlay for the recognized-barcode highlight, and drive the camera (frame source) yourself. Use BarcodeCapture when you need full control over the scanning surface (custom layouts, custom viewfinders, location selection, etc.).
Language note: Examples below use TypeScript (.tsx) because it is the default for React Native templates. For plain JavaScript projects, drop the type annotations and keep the same imports and structure.Prerequisites
- Scandit React Native packages installed:
scandit-react-native-datacapture-corescandit-react-native-datacapture-barcode- After installing, run
npx pod-install(orcd ios && pod install) for iOS. Android auto-links via Gradle — no manual step. - React Native
>=0.70. The New Architecture (Fabric / TurboModules) is supported — no additional setup required beyond the standard RN template. - A valid Scandit license key:
- Sign in at https://ssl.scandit.com to generate one.
- No account yet? Sign up at https://ssl.scandit.com/dashboard/sign-up?p=test.
- Camera permissions configured by the app:
- iOS: add
NSCameraUsageDescriptiontoios/<App>/Info.plist. - Android: the manifest permission is declared by the plugin; request at runtime via
PermissionsAndroid.request(PermissionsAndroid.PERMISSIONS.CAMERA)before rendering the scan screen.
Integration flow
Ask the user which barcode symbologies they need to scan. When asking, mention that it's important to only enable the symbologies they actually need, as enabling fewer improves scanning performance and accuracy.
Once the user responds, ask them which file they'd like to integrate BarcodeCapture into (typically the scan screen component, e.g. App.tsx, ScanScreen.tsx, or a page module). Then write the integration code directly into that file. Do not just show the code in chat; apply it to the file.
After providing the code, show this setup checklist:
Setup checklist: 1. Install packages: npm install scandit-react-native-datacapture-core scandit-react-native-datacapture-barcode 2. Run npx pod-install (iOS). Android auto-links. 3. Add NSCameraUsageDescription to ios/<App>/Info.plist. 4. Replace -- ENTER YOUR SCANDIT LICENSE KEY HERE -- with your key from https://ssl.scandit.com. 5. If Metro was running, restart it with --reset-cache so the new package is picked up.
Step 1 — Initialize DataCaptureContext (singleton module)
Create a small module that initializes the context exactly once at import time and re-exports the singleton for the rest of the app:
// CaptureContext.ts
import { DataCaptureContext } from 'scandit-react-native-datacapture-core';
const licenseKey = '-- ENTER YOUR SCANDIT LICENSE KEY HERE --';
DataCaptureContext.initialize(licenseKey);
export default DataCaptureContext.sharedInstance;DataCaptureContext.initialize(licenseKey)is the v8 API. It is idempotent per process — call it once.DataCaptureContext.sharedInstanceis the singleton accessor used everywhere else in the app.- Do not create additional
DataCaptureContextinstances — there is only one per app.
Important: Put theinitialize(...)call at the top of a dedicated module so it runs on first import and before any component that usessharedInstancemounts.
Step 2 — Configure BarcodeCaptureSettings + symbologies
Choose which barcode symbologies to scan. Only enable what you need — each extra symbology adds processing time. By default all symbologies are disabled.
import {
BarcodeCaptureSettings,
Symbology,
} from 'scandit-react-native-datacapture-barcode';
const settings = new BarcodeCaptureSettings();
settings.enableSymbologies([
Symbology.EAN13UPCA,
Symbology.EAN8,
Symbology.UPCE,
Symbology.Code39,
Symbology.Code128,
Symbology.InterleavedTwoOfFive,
]);
// Optional: adjust active symbol counts for variable-length symbologies
const code39Settings = settings.settingsForSymbology(Symbology.Code39);
code39Settings.activeSymbolCounts = [7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20];Per-symbology settings (settingsForSymbology)
settings.settingsForSymbology(Symbology.X) returns the mutable SymbologySettings for one symbology. Mutating it updates the parent BarcodeCaptureSettings. Apply the result with barcodeCapture.applySettings(settings) (or pass settings into the constructor before the mode is added). Common per-symbology configuration:
const code39Settings = settings.settingsForSymbology(Symbology.Code39);
// Extensions — symbology-specific feature flags (string keys).
code39Settings.setExtensionEnabled('full_ascii', true);
// Checksums — array of Checksum values. The code is accepted if any matches.
code39Settings.checksums = [Checksum.Mod43];
// Active symbol counts — allowed code lengths (in symbols) for variable-length codes.
code39Settings.activeSymbolCounts = [7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20];
// Color-inverted codes — read bright codes on a dark background.
code39Settings.isColorInvertedEnabled = true;| Member | Type | Description |
|---|---|---|
setExtensionEnabled(extension, enabled) | method | Enable/disable a symbology extension by string key (e.g. 'full_ascii', 'remove_leading_zero'). |
isExtensionEnabled(extension) | method | Whether an extension is enabled. |
checksums | Checksum[] | Optional checksums (e.g. Checksum.Mod43). Imported from scandit-react-native-datacapture-barcode. |
activeSymbolCounts | number[] | Allowed lengths in symbols for variable-length symbologies. |
isColorInvertedEnabled | boolean | Enable decoding of color-inverted (bright-on-dark) codes for this symbology. |
Checksum is imported from scandit-react-native-datacapture-barcode.
BarcodeCaptureSettings Properties
| Property | Type | Description |
|---|---|---|
codeDuplicateFilter | number | Milliseconds to suppress duplicate scans of the same code. 0 = report every detection, -1 = report each code only once until the mode is disabled. |
scanIntention | ScanIntention | Scanning intent mode. Values: ScanIntention.Smart (default in v7+), ScanIntention.Manual. |
batterySaving | BatterySavingMode | Battery optimization. Auto, On, or Off. |
locationSelection | `LocationSelection \ | null` |
enabledCompositeTypes | CompositeType[] | Composite barcode types. |
enabledSymbologies | Symbology[] | Read-only set of currently enabled symbologies. |
BarcodeCaptureSettings Methods
| Method | Description |
|---|---|
enableSymbologies(symbologies) | Enable multiple symbologies. |
enableSymbology(symbology, enabled) | Enable or disable one. |
settingsForSymbology(symbology) | Get per-symbology settings (e.g., activeSymbolCounts). |
enableSymbologiesForCompositeTypes(compositeTypes) | Enable symbologies required for composite types. |
setProperty(name, value) / getProperty(name) | Advanced property access by name. |
Step 3 — Create the Camera and set it as the frame source
BarcodeCapture has no built-in camera. Create a Camera, apply the recommended camera settings for barcode capture, and set it as the context's frame source.
import {
Camera,
FrameSourceState,
} from 'scandit-react-native-datacapture-core';
import { BarcodeCapture } from 'scandit-react-native-datacapture-barcode';
const camera = Camera.default;
camera?.applySettings(BarcodeCapture.recommendedCameraSettings);
await dataCaptureContext.setFrameSource(camera);Camera.default returns the world-facing camera. BarcodeCapture.recommendedCameraSettings is a static getter that returns a CameraSettings tuned for barcode scanning. The camera is started later by switching it to FrameSourceState.On (Step 7).
Step 4 — Create the BarcodeCapture mode
import {
BarcodeCapture,
BarcodeCaptureSettings,
} from 'scandit-react-native-datacapture-barcode';
// v8 (preferred from 7.6+)
const barcodeCapture = new BarcodeCapture(settings);
dataCaptureContext.addMode(barcodeCapture);
// v7 / v6 — still supported in v8 but discouraged in new code
// const barcodeCapture = BarcodeCapture.forContext(dataCaptureContext, settings);new BarcodeCapture(settings) does not bind to a context; you must call dataCaptureContext.addMode(barcodeCapture) (or setMode(...)) to attach it. BarcodeCapture.forContext(context, settings) is the older factory that auto-attaches when context is non-null.
BarcodeCapture Methods and Properties
| Member | Type | Description |
|---|---|---|
isEnabled | boolean | Pause / resume scanning without stopping the camera. Set to false inside didScan while you process the result. |
feedback | BarcodeCaptureFeedback | Sound + vibration feedback. See Step 8. |
applySettings(settings) | Promise<void> | Update settings at runtime. |
addListener(listener) / removeListener(listener) | — | Register or remove a BarcodeCaptureListener. |
BarcodeCapture.recommendedCameraSettings | CameraSettings | Static — recommended camera settings for barcode capture. |
Step 5 — Render <DataCaptureView> and add a BarcodeCaptureOverlay
The DataCaptureView renders the camera preview. The BarcodeCaptureOverlay draws the recognized-barcode highlight on top.
import React, { useEffect, useRef } from 'react';
import {
DataCaptureView,
} from 'scandit-react-native-datacapture-core';
import {
BarcodeCaptureOverlay,
} from 'scandit-react-native-datacapture-barcode';
const viewRef = useRef<DataCaptureView | null>(null);
useEffect(() => {
// v8 constructor
const overlay = new BarcodeCaptureOverlay(barcodeCapture);
viewRef.current?.addOverlay(overlay);
return () => {
viewRef.current?.removeOverlay(overlay);
};
}, []);
return (
<DataCaptureView
style={{ flex: 1 }}
context={dataCaptureContext}
ref={view => { viewRef.current = view; }}
/>
);The legacy factory BarcodeCaptureOverlay.withBarcodeCaptureForView(barcodeCapture, view) is still available — passing a non-null view adds the overlay automatically.
BarcodeCaptureOverlay Properties
| Property | Type | Description |
|---|---|---|
viewfinder | `Viewfinder \ | null` |
brush | Brush | The brush used to highlight recognized barcodes. Default: transparent fill, Scandit-blue stroke, width 1. |
shouldShowScanAreaGuides | boolean | Development aid — shows the scan area outline. Default false. Do not use in production. |
Step 6 — Implement BarcodeCaptureListener
The listener delivers barcode results. On React Native, both methods are async and receive a getFrameData thunk you can await to retrieve frame bytes if needed.
import {
BarcodeCapture,
BarcodeCaptureSession,
} from 'scandit-react-native-datacapture-barcode';
import { FrameData } from 'scandit-react-native-datacapture-core';
const listener = {
didScan: async (
mode: BarcodeCapture,
session: BarcodeCaptureSession,
_getFrameData: () => Promise<FrameData>,
) => {
const barcode = session.newlyRecognizedBarcode;
if (barcode == null) return;
// Pause scanning while we process this result.
mode.isEnabled = false;
// ... handle the barcode (navigate, network call, UI update)
// Re-enable when ready for the next scan, or leave disabled to stop.
mode.isEnabled = true;
},
didUpdateSession: async (
_mode: BarcodeCapture,
_session: BarcodeCaptureSession,
_getFrameData: () => Promise<FrameData>,
) => {
// Called every processed frame, regardless of whether a code was scanned.
},
};
barcodeCapture.addListener(listener);BarcodeCaptureListener Interface
All callbacks are optional. Implement only what you need.
| Callback | Signature (React Native) | Description |
|---|---|---|
didScan | (barcodeCapture, session, getFrameData) => Promise<void> | Called when a barcode is scanned. |
didUpdateSession | (barcodeCapture, session, getFrameData) => Promise<void> | Called on every frame processed. |
BarcodeCaptureSession Properties
| Property | Type | Description |
|---|---|---|
newlyRecognizedBarcode | `Barcode \ | null` |
newlyLocalizedBarcodes | LocalizedOnlyBarcode[] | Barcodes that were localized but not decoded this frame. |
frameSequenceID | number | Frame sequence identifier; stays the same as long as frames flow without interruption. |
reset() | Promise<void> | Clear session history (resets duplicate filtering). Only call inside a listener callback. |
Important: Do not retain references tosession.newlyRecognizedBarcodeorsession.newlyLocalizedBarcodesoutside the listener callback — the session is mutated concurrently.
Step 7 — Lifecycle: switch the camera on / off
The camera is not started by mounting <DataCaptureView>. You drive it explicitly via FrameSourceState. The recommended pattern with React Navigation is useFocusEffect:
import { useFocusEffect } from '@react-navigation/native';
import { useCallback } from 'react';
import { Camera, FrameSourceState } from 'scandit-react-native-datacapture-core';
useFocusEffect(
useCallback(() => {
barcodeCapture.isEnabled = true;
Camera.default?.switchToDesiredState(FrameSourceState.On);
return () => {
barcodeCapture.isEnabled = false;
Camera.default?.switchToDesiredState(FrameSourceState.Off);
};
}, []),
);Cleanup responsibilities when the scan screen unmounts:
| What | How |
|---|---|
| Stop the camera | camera.switchToDesiredState(FrameSourceState.Off) |
| Disable the mode | barcodeCapture.isEnabled = false |
| Remove the overlay | dataCaptureView.removeOverlay(overlay) |
| Unbind the mode from the context | dataCaptureContext.removeMode(barcodeCapture) |
| Remove the listener | barcodeCapture.removeListener(listener) |
### ⚠️ Never call dataCaptureContext.dispose()>
The context is a process-wide singleton (DataCaptureContext.sharedInstance). Disposing it tears down every Scandit screen in the entire app, not just the one being unmounted, and leaves the app unable to scan again until the JS bundle is reloaded.>
Do not includedataCaptureContext.dispose()inuseEffectcleanup,useFocusEffectcleanup,componentWillUnmount, or anywhere else — even if your training data or other React Native libraries follow that convention. The complete and correct unmount cleanup is exactly the five rows in the table above (camera off, mode disabled, overlay removed, mode removed, listener removed). Stop afterremoveModeandremoveListener. Addingdispose()is always a bug.
Step 8 — Feedback customization
By default BarcodeCapture plays a beep and vibrates on every successful scan. The feedback object is mutable on the mode:
import { Feedback, Vibration } from 'scandit-react-native-datacapture-core';
import { BarcodeCaptureFeedback } from 'scandit-react-native-datacapture-barcode';
const feedback = BarcodeCaptureFeedback.defaultFeedback;
// Vibration only, no sound:
feedback.success = new Feedback(Vibration.defaultVibration, null);
barcodeCapture.feedback = feedback;BarcodeCaptureFeedback.success is a Feedback with optional Vibration and Sound components. Passing null for either disables that channel.
Step 9 — Viewfinder (optional)
A viewfinder is a visual cue painted by BarcodeCaptureOverlay. It is purely cosmetic and does not affect what is scanned. Set it on the overlay:
import {
RectangularViewfinder,
RectangularViewfinderStyle,
RectangularViewfinderLineStyle,
LaserlineViewfinder,
} from 'scandit-react-native-datacapture-core';
// Rectangular viewfinder
overlay.viewfinder = new RectangularViewfinder(
RectangularViewfinderStyle.Square,
RectangularViewfinderLineStyle.Light,
);
// Or a laserline viewfinder (no constructor arguments)
// overlay.viewfinder = new LaserlineViewfinder();
// Or an aimer viewfinder (a crosshair-style dot + frame, good for single-code aiming)
// const aimer = new AimerViewfinder();
// aimer.frameColor = Color.fromHex('#FFFFFF');
// aimer.dotColor = Color.fromHex('#FF0000');
// overlay.viewfinder = aimer;AimerViewfinder is imported from scandit-react-native-datacapture-core. It has no constructor arguments; customize it through its frameColor and dotColor properties (both Color). LaserlineViewfinder also takes no constructor arguments; customize via its width (NumberWithUnit), enabledColor, and disabledColor properties.
To restrict where barcodes are accepted (not just where the viewfinder is drawn), see Step 10.
Step 9b — Overlay brush (highlight color)
The brush property of BarcodeCaptureOverlay controls how recognized barcodes are highlighted. Construct a Brush(fillColor, strokeColor, strokeWidth):
import { Brush, Color } from 'scandit-react-native-datacapture-core';
overlay.brush = new Brush(
Color.fromRGBA(0, 255, 0, 0.2), // fill
Color.fromHex('#00FF00'), // stroke
2, // stroke width
);Brush, Color are imported from scandit-react-native-datacapture-core. Brush.transparent is a static that returns a fully transparent brush (no fill, no stroke) — useful to hide a highlight (see "Rejecting barcodes" below).
Step 9c — Rejecting barcodes (visually mark unwanted codes)
To reject a scanned barcode — accept only codes that match a rule — handle the rejection inside didScan. The pattern: in didScan, inspect barcode.data; if it does not match your rule, set the overlay's brush to Brush.transparent so the rejected code is not highlighted, and return early without processing it. Acceptable codes get the normal brush.
const listener = {
didScan: async (mode, session) => {
const barcode = session.newlyRecognizedBarcode;
if (barcode == null) return;
// Reject anything that is not an internal SKU.
if (!barcode.data?.startsWith('SKU-')) {
overlay.brush = Brush.transparent;
return; // do not process the rejected code
}
overlay.brush = new Brush(
Color.fromRGBA(0, 255, 0, 0.2),
Color.fromHex('#00FF00'),
2,
);
// ... process the accepted barcode
},
};Step 10 — Location selection (optional)
locationSelection on BarcodeCaptureSettings filters which barcodes are accepted by where they appear in the frame. Apply it via barcodeCapture.applySettings(settings).
import {
RadiusLocationSelection,
NumberWithUnit,
MeasureUnit,
} from 'scandit-react-native-datacapture-core';
settings.locationSelection = new RadiusLocationSelection(
new NumberWithUnit(0.5, MeasureUnit.Fraction),
);
await barcodeCapture.applySettings(settings);A rectangular location selection (RectangularLocationSelection.withSize(...)) is also available if you need a non-circular acceptance region.
Step 11 — Scan intention (optional)
ScanIntention.Smart (default in v7+) intelligently picks the barcode the user is aiming at when several are visible. ScanIntention.Manual reverts to scanning whatever decodes first.
import { ScanIntention } from 'scandit-react-native-datacapture-core';
settings.scanIntention = ScanIntention.Manual;
await barcodeCapture.applySettings(settings);Step 12 — CodeDuplicateFilter (optional)
To suppress repeated reports of the same barcode value within a time window, set codeDuplicateFilter (in milliseconds) on the settings before applying:
settings.codeDuplicateFilter = 500; // 500 ms
await barcodeCapture.applySettings(settings);0— report every detection.-1— report each code at most once until the mode is disabled.- positive value — minimum interval between repeated reports of the same code.
Step 13 — Composite codes (optional)
To scan composite codes (a 1D code + a 2D component), enable both the composite types and the symbologies they require:
import { CompositeType } from 'scandit-react-native-datacapture-barcode';
settings.enabledCompositeTypes = [CompositeType.A, CompositeType.B];
settings.enableSymbologiesForCompositeTypes(settings.enabledCompositeTypes);Step 14 — Camera Permissions
iOS
Add to ios/<App>/Info.plist:
<key>NSCameraUsageDescription</key>
<string>We need camera access to scan barcodes</string>Android
Request at runtime before the scan screen mounts:
import { Platform, PermissionsAndroid } from 'react-native';
async function requestCameraPermission() {
if (Platform.OS !== 'android') return true;
const status = await PermissionsAndroid.request(
PermissionsAndroid.PERMISSIONS.CAMERA,
);
return status === PermissionsAndroid.RESULTS.GRANTED;
}Gate navigation to the scan screen on a successful permission:
const handleStartScan = async () => {
const granted = await requestCameraPermission();
if (!granted) return;
navigation.navigate('scan');
};Step 15 — Complete Example
Full working scan screen: context singleton, camera, mode, overlay, listener, lifecycle.
CaptureContext.ts
import { DataCaptureContext } from 'scandit-react-native-datacapture-core';
const licenseKey = '-- ENTER YOUR SCANDIT LICENSE KEY HERE --';
DataCaptureContext.initialize(licenseKey);
export default DataCaptureContext.sharedInstance;ScanScreen.tsx
import React, { useCallback, useEffect, useRef, useState } from 'react';
import { View, Text, StyleSheet } from 'react-native';
import { useFocusEffect } from '@react-navigation/native';
import {
Camera,
DataCaptureView,
FrameSourceState,
} from 'scandit-react-native-datacapture-core';
import {
BarcodeCapture,
BarcodeCaptureOverlay,
BarcodeCaptureSession,
BarcodeCaptureSettings,
Symbology,
SymbologyDescription,
} from 'scandit-react-native-datacapture-barcode';
import dataCaptureContext from './CaptureContext';
export const ScanScreen = () => {
const [lastScan, setLastScan] = useState<string | null>(null);
const viewRef = useRef<DataCaptureView | null>(null);
const cameraRef = useRef<Camera | null>(null);
const barcodeCaptureRef = useRef<BarcodeCapture | null>(null);
if (cameraRef.current === null) {
const camera = Camera.default;
camera?.applySettings(BarcodeCapture.recommendedCameraSettings);
dataCaptureContext.setFrameSource(camera);
cameraRef.current = camera;
}
if (barcodeCaptureRef.current === null) {
const settings = new BarcodeCaptureSettings();
settings.enableSymbologies([
Symbology.EAN13UPCA,
Symbology.EAN8,
Symbology.UPCE,
Symbology.Code39,
Symbology.Code128,
Symbology.InterleavedTwoOfFive,
]);
const barcodeCapture = new BarcodeCapture(settings);
dataCaptureContext.addMode(barcodeCapture);
barcodeCaptureRef.current = barcodeCapture;
}
useEffect(() => {
const barcodeCapture = barcodeCaptureRef.current!;
const listener = {
didScan: async (
mode: BarcodeCapture,
session: BarcodeCaptureSession,
) => {
const barcode = session.newlyRecognizedBarcode;
if (barcode == null) return;
mode.isEnabled = false;
const symbology = new SymbologyDescription(barcode.symbology);
setLastScan(`${barcode.data} (${symbology.readableName})`);
mode.isEnabled = true;
},
};
barcodeCapture.addListener(listener);
const overlay = new BarcodeCaptureOverlay(barcodeCapture);
viewRef.current?.addOverlay(overlay);
return () => {
viewRef.current?.removeOverlay(overlay);
barcodeCapture.removeListener(listener);
dataCaptureContext.removeMode(barcodeCapture);
};
}, []);
useFocusEffect(
useCallback(() => {
const barcodeCapture = barcodeCaptureRef.current!;
const camera = cameraRef.current;
barcodeCapture.isEnabled = true;
camera?.switchToDesiredState(FrameSourceState.On);
return () => {
barcodeCapture.isEnabled = false;
camera?.switchToDesiredState(FrameSourceState.Off);
};
}, []),
);
return (
<View style={styles.container}>
<DataCaptureView
style={styles.preview}
context={dataCaptureContext}
ref={view => { viewRef.current = view; }}
/>
<View style={styles.results}>
<Text>{lastScan ?? 'Waiting for scan...'}</Text>
</View>
</View>
);
};
const styles = StyleSheet.create({
container: { flex: 1 },
preview: { flex: 1 },
results: { padding: 16, backgroundColor: '#fff' },
});Key Rules
1. Singleton context — Call DataCaptureContext.initialize(licenseKey) once in a dedicated module and export DataCaptureContext.sharedInstance. Never construct another context anywhere else. 2. Function components with hooks — Hold the camera, mode, and view in useRef. Register listeners and overlays inside useEffect. Drive the camera on/off through useFocusEffect. Do not use class components for new BarcodeCapture code. 3. Camera is not automatic — Camera.default + dataCaptureContext.setFrameSource(camera) + camera.switchToDesiredState(FrameSourceState.On) are all required. The native view does not start the camera for you. 4. Disable the mode in `didScan` — Set barcodeCapture.isEnabled = false before doing per-scan work to avoid duplicate callbacks. Re-enable when ready for the next code. 5. Cleanup on unmount — Stop the camera, remove the overlay, remove the listener, and call dataCaptureContext.removeMode(barcodeCapture). Do not call dataCaptureContext.dispose(). 6. Construction in v8 — Prefer new BarcodeCapture(settings) + context.addMode(...). The BarcodeCapture.forContext(context, settings) factory is older but still works. 7. Imports — Core types (DataCaptureContext, DataCaptureView, Camera, FrameSourceState, Color, Brush, viewfinders) from scandit-react-native-datacapture-core; barcode types (BarcodeCapture, BarcodeCaptureSettings, BarcodeCaptureOverlay, Symbology, SymbologyDescription, BarcodeCaptureFeedback) from scandit-react-native-datacapture-barcode. 8. Pod install — Run npx pod-install (or cd ios && pod install) after installing or updating Scandit packages. Android auto-links. 9. Camera permissions — iOS: NSCameraUsageDescription in Info.plist. Android: runtime request via PermissionsAndroid before navigating to the scan screen. 10. Metro cache — If a package upgrade appears to have no effect at runtime, restart Metro with npm start -- --reset-cache.
For the full API surface (every property, every overload), see the BarcodeCapture API reference.
BarcodeCapture React Native Migration Guide
Step 1: Detect the installed SDK version
Before making any changes, find out which version of the Scandit React Native packages the project currently has installed.
Check in this order:
1. `package.json` — look for the entries scandit-react-native-datacapture-core and/or scandit-react-native-datacapture-barcode. The value next to them is the installed version constraint (e.g. "^6.28.0", "~7.6.0", "^8.0.0"). 2. `package-lock.json` or `yarn.lock` — if package.json only has a range, check the lockfile for the exact resolved version.
Once you know the installed version, determine which migration path applies:
| Installed version | Target version | Action |
|---|---|---|
| 6.x | 7.x | Apply the 6 → 7 migration below |
| 7.x | 8.x | Apply the 7 → 8 migration below |
| 6.x | 8.x | Apply both migrations in order (6→7 first, then 7→8) |
If neither package is in package.json, the project is not using BarcodeCapture on React Native yet — fall back to references/integration.md instead of migrating.
---
Step 2: Update the package version
Before touching source files, update the Scandit package versions in package.json:
{
"dependencies": {
"scandit-react-native-datacapture-core": "^8.0.0",
"scandit-react-native-datacapture-barcode": "^8.0.0"
}
}Then install and re-link native projects:
npm install
npx pod-install # iOS — required after every plugin version changenpx pod-install is required on iOS after every plugin version change — it resolves the new CocoaPods artifacts into ios/Podfile.lock. Android auto-links via Gradle; a regular npx react-native run-android picks up the new version.
If Metro is running, restart it with --reset-cache so the JavaScript bundle reflects the new package.
---
Step 3: Apply source code changes
Find the files that use BarcodeCapture (search the project for BarcodeCapture, BarcodeCaptureSettings, BarcodeCaptureOverlay, BarcodeCaptureListener) and apply the relevant changes below directly to those files.
---
Migration: 6 → 7
Scan intention default change
The default scan intention for BarcodeCaptureSettings is now ScanIntention.Smart (as of v7). The Smart algorithm intelligently identifies and scans the barcode the user intends to capture when several are visible.
- If the project explicitly set
settings.scanIntention = ScanIntention.Manualor another value, leave it as is. - If the project relied on the v6 default (which was effectively
Manual), the code still runs but behavior changes. Tell the user about the change and let them opt back intoScanIntention.Manualif they prefer the old behavior.
codeDuplicateFilter default change
From SDK 7.1, the default codeDuplicateFilter value is the special -2 sentinel — its behavior depends on scanIntention:
- With
ScanIntention.Smart(default), it enables the Smart Duplicate Filter algorithm. - With
ScanIntention.Manual, it behaves as ifcodeDuplicateFilterwere set to 1500 ms.
If the project explicitly set codeDuplicateFilter to a positive value, 0, or -1, leave it. If it relied on the v6 default, mention the change to the user — no code change required.
BarcodeTracking → BarcodeBatch rename
If the project uses BarcodeTracking (MatrixScan) alongside BarcodeCapture, rename all occurrences to BarcodeBatch. Imports from scandit-react-native-datacapture-barcode need updating. The API is otherwise unchanged.
No source-level renames on BarcodeCapture itself
The BarcodeCapture, BarcodeCaptureSettings, BarcodeCaptureOverlay, and BarcodeCaptureListener surfaces are stable across 6 → 7. The forContext factory, the didScan / didUpdateSession listener signatures, and the overlay's viewfinder, brush, and shouldShowScanAreaGuides properties all remain valid.
If the v6 code looks structurally correct after the package bump and the points above, no further source edits are needed for 6 → 7 of BarcodeCapture.
---
Migration: 7 → 8
DataCaptureContext.forLicenseKey → DataCaptureContext.initialize
This is the main breaking change on React Native in v8. The context factory method was renamed and its semantics tightened — it no longer returns the instance directly; the singleton is read from DataCaptureContext.sharedInstance.
v7:
const context = DataCaptureContext.forLicenseKey('YOUR_LICENSE_KEY');v8:
DataCaptureContext.initialize('YOUR_LICENSE_KEY');
const context = DataCaptureContext.sharedInstance;Replace every call to DataCaptureContext.forLicenseKey(...) with DataCaptureContext.initialize(...) and replace subsequent references to the returned context with DataCaptureContext.sharedInstance. This call must still happen before any other Scandit API.
A common v8 idiom is to put both lines in a small CaptureContext.ts module and export DataCaptureContext.sharedInstance as the default export — see references/integration.md step 1.
Capture mode factory deprecation: BarcodeCapture.forContext → new BarcodeCapture + addMode
The BarcodeCapture.forContext(context, settings) factory is deprecated in v8. The v8 idiom is to construct the mode directly and attach it to the context yourself.
v7:
const barcodeCapture = BarcodeCapture.forContext(dataCaptureContext, settings);v8:
const barcodeCapture = new BarcodeCapture(settings);
dataCaptureContext.addMode(barcodeCapture);The forContext form still works in v8 for backwards compatibility, but new code should use the constructor + addMode form.
The same pattern applies to other capture modes the project may use:
BarcodeBatch.forContext(context, settings)→new BarcodeBatch(settings)+context.addMode(...)BarcodeSelection.forContext(context, settings)→new BarcodeSelection(settings)+context.addMode(...)
BarcodeCaptureOverlay.withBarcodeCaptureForView → new BarcodeCaptureOverlay + addOverlay
The overlay has a v7.6+ constructor that mirrors the mode pattern.
v7:
const overlay = BarcodeCaptureOverlay.withBarcodeCaptureForView(barcodeCapture, dataCaptureView);v8:
const overlay = new BarcodeCaptureOverlay(barcodeCapture);
dataCaptureView.addOverlay(overlay);The legacy factory still works — passing a non-null view auto-attaches.
Cleanup: dataCaptureContext.dispose() → dataCaptureContext.removeMode(barcodeCapture)
In v7, a common pattern was calling dataCaptureContext.dispose() in the useEffect cleanup. In v8, the context is a process-wide singleton (DataCaptureContext.sharedInstance) and should not be disposed when a single screen unmounts — doing so would break any other Scandit screen mounted in the same app.
Replace:
// v7
return () => {
dataCaptureContext.dispose();
};with:
// v8
return () => {
dataCaptureView.removeOverlay(overlay);
barcodeCapture.removeListener(listener);
dataCaptureContext.removeMode(barcodeCapture);
};This unbinds the BarcodeCapture mode from the shared context without tearing the context itself down.
New v8 APIs (optional, no action required unless the user wants them)
Available in v8 — mention only if the user asks:
BarcodeCaptureSettings.selectionMode(SelectionMode.Off | On | Auto) — explicit tap-to-confirm selection on top of barcode capture.useFocusEffectlifecycle pattern combined withCamera.switchToDesiredState(FrameSourceState.On / Off)is the v8 recommended way to drive the camera on RN.
---
After applying changes
1. Run npm install && npx pod-install again after any additional package changes triggered by the migration (e.g. if TypeScript type errors surface additional version mismatches). 2. Restart Metro with npm start -- --reset-cache so the bundle picks up the new package code. 3. Build the iOS and Android apps and fix any remaining compile / runtime errors using the API reference (linked in SKILL.md). 4. Let the user know they can check the full list of SDK changes in the official migration guides:
- 6 → 7: https://docs.scandit.com/sdks/react-native/migrate-6-to-7/
- 7 → 8: https://docs.scandit.com/sdks/react-native/migrate-7-to-8/
5. Show the user a summary of only the changes actually made: which files were edited, which calls were replaced, and anything that required a judgment call (e.g. whether a dataCaptureContext.dispose() call was converted to removeMode, whether forContext was rewritten to new BarcodeCapture + addMode). Do not list APIs that were already correct or unchanged. 6. If compile errors persist after the changes above, fetch the BarcodeCapture API reference (https://docs.scandit.com/data-capture-sdk/react-native/barcode-capture/api.html) to find the correct API before guessing.
Related skills
How it compares
Mobile SDK integration skill—not a generic Expo camera tutorial or server-side barcode API.
FAQ
Who is barcode-capture-rn for?
Developers shipping React Native apps who licensed Scandit and need agent-guided integration on a primary scan screen.
When should I use barcode-capture-rn?
During Build integrations while replacing a ‘Scanner goes here’ placeholder with BarcodeCapture, camera lifecycle, and symbology settings.
Is barcode-capture-rn safe to install?
It touches camera and commercial SDK credentials; review the Security Audits panel on this page and never commit real license keys to git.