React Native
Template Management
v3.0.0 Breaking Change: Template management has been completely redesigned. Templates are now applied via the template prop on VisionCamera, and template creation is handled entirely in React Native. See the Release Notes for migration details.
# What are Templates?
Templates define barcode matching patterns for scanning. Once created, a template contains reference barcodes that can be used to match against scanned codes during OCR operations, improving accuracy and filtering.
Templates allow you to:
- Define specific barcode patterns to detect
- Filter out unwanted barcode types
- Improve scanning accuracy for your use case
- Create reusable scanning configurations
# Stateless Architecture
Templates follow a stateless architecture - the SDK does not manage template storage. You are responsible for storing and managing templates in your app.
| Responsibility | SDK | Your App |
|---|---|---|
| Apply template to scanner | SDK (via template prop) | — |
| Store template data | — | You manage |
| Retrieve templates | — | You manage |
| Delete templates | — | You manage |
| Create template data | — | You manage (from detected barcodes) |
Benefits:
- More flexible - store templates anywhere (AsyncStorage, Redux, SQLite, remote backend)
- Better control - full ownership of template lifecycle
- Smaller SDK - no built-in storage management
- Portable - templates can be shared across devices
# Template Workflow
# 1. Detect Barcodes and Build Templates
In v3.0.0, template creation is handled entirely in React Native. Use the onBarcodeDetected event to collect barcodes for your template:
import React, { useRef, useState } from 'react';
import { View, Button, Text, TouchableOpacity } from 'react-native';
import AsyncStorage from '@react-native-async-storage/async-storage';
import { VisionCamera, VisionCameraRefProps } from 'react-native-vision-sdk';
import type { TemplateData, TemplateCode } from 'react-native-vision-sdk';
const TemplateCreationExample = () => {
const cameraRef = useRef<VisionCameraRefProps>(null);
const [isTemplateMode, setIsTemplateMode] = useState(false);
const [templateCodes, setTemplateCodes] = useState<TemplateCode[]>([]);
const [detectedBarcodes, setDetectedBarcodes] = useState<any[]>([]);
// Add a detected barcode to the template
const addBarcodeToTemplate = (code: {
scannedCode: string;
symbology: string;
boundingBox: any;
}) => {
const newCode: TemplateCode = {
codeString: code.scannedCode,
codeSymbology: code.symbology,
boundingBox: code.boundingBox,
};
setTemplateCodes((prev) => {
// Avoid duplicates
const exists = prev.some(
(c) => c.codeString === newCode.codeString && c.codeSymbology === newCode.codeSymbology
);
if (exists) return prev;
return [...prev, newCode];
});
};
// Save the template
const saveTemplate = async () => {
if (templateCodes.length === 0) return;
const template: TemplateData = {
id: `template_${Date.now()}`,
templateCodes,
};
await AsyncStorage.setItem(`template_${template.id}`, JSON.stringify(template));
setTemplateCodes([]);
setIsTemplateMode(false);
alert(`Template saved with ${template.templateCodes.length} code(s)`);
};
return (
<View style={{ flex: 1 }}>
<VisionCamera
ref={cameraRef}
scanMode="barcode"
onBarcodeDetected={(event) => {
if (isTemplateMode) {
setDetectedBarcodes(event.codes);
}
}}
/>
{/* Template creation UI */}
{isTemplateMode && (
<View style={{ position: 'absolute', bottom: 0, left: 0, right: 0, padding: 20, backgroundColor: 'rgba(0,0,0,0.8)' }}>
<Text style={{ color: '#fff', marginBottom: 10 }}>
Template: {templateCodes.length} code(s) selected
</Text>
{/* Show detected barcodes for selection */}
{detectedBarcodes.map((code, index) => (
<TouchableOpacity
key={index}
onPress={() => addBarcodeToTemplate(code)}
style={{ padding: 10, backgroundColor: '#333', marginBottom: 5, borderRadius: 5 }}
>
<Text style={{ color: '#fff' }}>
{code.symbology}: {code.scannedCode}
</Text>
</TouchableOpacity>
))}
<Button title="Save Template" onPress={saveTemplate} disabled={templateCodes.length === 0} />
<Button title="Cancel" onPress={() => { setTemplateCodes([]); setIsTemplateMode(false); }} />
</View>
)}
{!isTemplateMode && (
<Button title="Create Template" onPress={() => setIsTemplateMode(true)} />
)}
</View>
);
};
# 2. Apply Template to Scanner
To use a template during scanning, pass it via the template prop on VisionCamera:
import React, { useRef, useState, useEffect } from 'react';
import AsyncStorage from '@react-native-async-storage/async-storage';
import { VisionCamera, VisionCameraRefProps } from 'react-native-vision-sdk';
import type { TemplateData } from 'react-native-vision-sdk';
const ScannerWithTemplate = () => {
const cameraRef = useRef<VisionCameraRefProps>(null);
const [activeTemplate, setActiveTemplate] = useState<TemplateData | null>(null);
// Load and apply a template
const loadAndApplyTemplate = async (templateId: string) => {
try {
const templateJson = await AsyncStorage.getItem(`template_${templateId}`);
if (templateJson) {
const template = JSON.parse(templateJson);
setActiveTemplate(template);
console.log('Template applied successfully');
}
} catch (error) {
console.error('Failed to load template:', error);
}
};
return (
<VisionCamera
ref={cameraRef}
scanMode="barcode"
template={activeTemplate} // Apply template via prop
onBarcodeDetected={(event) => {
console.log('Detected barcodes:', event.codes);
}}
/>
);
};
# 3. Remove Active Template
To remove the currently active template, set it to null:
// Remove template from scanner
setActiveTemplate(null);
# Complete Example with State Management
import React, { useRef, useState, useEffect, useCallback } from 'react';
import { View, Button, FlatList, Text, Alert, TouchableOpacity, Modal } from 'react-native';
import AsyncStorage from '@react-native-async-storage/async-storage';
import { VisionCamera, VisionCameraRefProps } from 'react-native-vision-sdk';
import type { TemplateData, TemplateCode } from 'react-native-vision-sdk';
const TEMPLATES_STORAGE_KEY = '@vision_sdk_templates';
const TemplateManagementExample = () => {
const cameraRef = useRef<VisionCameraRefProps>(null);
// Template state
const [savedTemplates, setSavedTemplates] = useState<TemplateData[]>([]);
const [activeTemplate, setActiveTemplate] = useState<TemplateData | null>(null);
// Template creation state
const [isTemplateMode, setIsTemplateMode] = useState(false);
const [templateCodes, setTemplateCodes] = useState<TemplateCode[]>([]);
const [detectedBarcodes, setDetectedBarcodes] = useState<any[]>([]);
const [showTemplateManager, setShowTemplateManager] = useState(false);
// Load all templates from storage
const loadTemplates = useCallback(async () => {
try {
const json = await AsyncStorage.getItem(TEMPLATES_STORAGE_KEY);
if (json) {
setSavedTemplates(JSON.parse(json));
}
} catch (error) {
console.error('Failed to load templates:', error);
}
}, []);
// Persist templates to storage
const persistTemplates = useCallback(async (templates: TemplateData[]) => {
try {
await AsyncStorage.setItem(TEMPLATES_STORAGE_KEY, JSON.stringify(templates));
setSavedTemplates(templates);
} catch (error) {
console.error('Failed to save templates:', error);
}
}, []);
// Add barcode to template being created
const handleAddBarcodeToTemplate = useCallback((code: {
scannedCode: string;
symbology: string;
boundingBox: { x: number; y: number; width: number; height: number };
}) => {
setTemplateCodes(prev => {
const alreadyExists = prev.some(
c => c.codeString === code.scannedCode && c.codeSymbology === code.symbology
);
if (alreadyExists) return prev;
return [...prev, {
codeString: code.scannedCode,
codeSymbology: code.symbology,
boundingBox: code.boundingBox
}];
});
}, []);
// Save the template
const handleSaveTemplate = useCallback(async () => {
if (templateCodes.length === 0) return;
const newTemplate: TemplateData = {
id: `template_${Date.now()}`,
templateCodes,
};
const updated = [...savedTemplates, newTemplate];
await persistTemplates(updated);
setTemplateCodes([]);
setIsTemplateMode(false);
Alert.alert('Template Saved', `Template saved with ${newTemplate.templateCodes.length} code(s).`);
}, [templateCodes, savedTemplates, persistTemplates]);
// Apply/remove template
const handleApplyTemplate = useCallback((template: TemplateData) => {
if (activeTemplate?.id === template.id) {
setActiveTemplate(null);
} else {
setActiveTemplate(template);
}
setShowTemplateManager(false);
}, [activeTemplate]);
// Delete a template
const handleDeleteTemplate = useCallback(async (id: string) => {
if (activeTemplate?.id === id) {
setActiveTemplate(null);
}
const updated = savedTemplates.filter(t => t.id !== id);
await persistTemplates(updated);
}, [savedTemplates, persistTemplates, activeTemplate]);
// Load templates on mount
useEffect(() => {
loadTemplates();
}, [loadTemplates]);
return (
<View style={{ flex: 1 }}>
<VisionCamera
ref={cameraRef}
scanMode="barcode"
template={activeTemplate}
onBarcodeDetected={(event) => {
if (isTemplateMode) {
setDetectedBarcodes(event.codes);
}
}}
onCapture={(event) => console.log('Captured:', event)}
/>
{/* Template Manager Button */}
<TouchableOpacity
style={{ position: 'absolute', top: 50, right: 20, padding: 10, backgroundColor: '#007AFF', borderRadius: 8 }}
onPress={() => setShowTemplateManager(true)}
>
<Text style={{ color: '#fff' }}>Templates</Text>
</TouchableOpacity>
{/* Active Template Indicator */}
{activeTemplate && (
<View style={{ position: 'absolute', top: 50, left: 20, padding: 10, backgroundColor: 'rgba(0,128,0,0.8)', borderRadius: 8 }}>
<Text style={{ color: '#fff' }}>Template Active</Text>
</View>
)}
{/* Template Creation Panel */}
{isTemplateMode && (
<View style={{ position: 'absolute', bottom: 0, left: 0, right: 0, padding: 20, backgroundColor: 'rgba(0,0,0,0.9)' }}>
<Text style={{ color: '#fff', fontSize: 16, marginBottom: 10 }}>
Creating Template ({templateCodes.length} codes)
</Text>
{detectedBarcodes.map((code, index) => (
<TouchableOpacity
key={index}
onPress={() => handleAddBarcodeToTemplate(code)}
style={{ padding: 10, backgroundColor: '#333', marginBottom: 5, borderRadius: 5 }}
>
<Text style={{ color: '#fff' }}>
Tap to add: {code.symbology} - {code.scannedCode}
</Text>
</TouchableOpacity>
))}
<View style={{ flexDirection: 'row', gap: 10, marginTop: 10 }}>
<Button title="Save Template" onPress={handleSaveTemplate} disabled={templateCodes.length === 0} />
<Button title="Cancel" onPress={() => { setTemplateCodes([]); setIsTemplateMode(false); }} />
</View>
</View>
)}
{/* Template Manager Modal */}
<Modal visible={showTemplateManager} animationType="slide" transparent>
<View style={{ flex: 1, justifyContent: 'flex-end', backgroundColor: 'rgba(0,0,0,0.5)' }}>
<View style={{ backgroundColor: '#1a1a2e', padding: 20, borderTopLeftRadius: 20, borderTopRightRadius: 20, maxHeight: '60%' }}>
<Text style={{ color: '#fff', fontSize: 18, marginBottom: 15 }}>Template Manager</Text>
<Button title="+ Create New Template" onPress={() => {
setShowTemplateManager(false);
setTemplateCodes([]);
setIsTemplateMode(true);
}} />
<FlatList
data={savedTemplates}
keyExtractor={(item) => item.id}
style={{ marginTop: 15 }}
renderItem={({ item }) => (
<View style={{ flexDirection: 'row', padding: 10, alignItems: 'center', borderBottomWidth: 1, borderBottomColor: '#333' }}>
<View style={{ flex: 1 }}>
<Text style={{ color: '#fff' }}>
{item.id.substring(0, 20)}...
</Text>
<Text style={{ color: '#888', fontSize: 12 }}>
{item.templateCodes.length} code(s)
{activeTemplate?.id === item.id ? ' • Active' : ''}
</Text>
</View>
<Button
title={activeTemplate?.id === item.id ? 'Remove' : 'Apply'}
onPress={() => handleApplyTemplate(item)}
/>
<View style={{ width: 10 }} />
<Button title="Delete" onPress={() => handleDeleteTemplate(item.id)} color="#cc0000" />
</View>
)}
ListEmptyComponent={
<Text style={{ color: '#888', textAlign: 'center', marginTop: 20 }}>
No templates saved
</Text>
}
/>
<Button title="Close" onPress={() => setShowTemplateManager(false)} />
</View>
</View>
</Modal>
</View>
);
};
export default TemplateManagementExample;
# Migration from v2.x to v3.0.0
# Before (v2.x - VisionSdkView)
// OLD - VisionSdkView methods removed in v3.0
visionSdk.current.createTemplate(); // REMOVED
visionSdk.current.getAllTemplates(); // REMOVED
visionSdk.current.deleteTemplateWithId(id); // REMOVED
visionSdk.current.deleteAllTemplates(); // REMOVED
// OLD - Applied via setObjectDetectionSettings
visionSdk.current.setObjectDetectionSettings({
selectedTemplate: templateJsonString
});
// OLD - VisionSdkView events
onCreateTemplate={(event) => {
const template = event.nativeEvent.data;
}}
# After (v3.0.0 - VisionCamera)
// NEW - Build templates from detected barcodes in React Native
const [templateCodes, setTemplateCodes] = useState<TemplateCode[]>([]);
<VisionCamera
scanMode="barcode"
onBarcodeDetected={(event) => {
// Add detected barcodes to your template
event.codes.forEach(code => {
setTemplateCodes(prev => [...prev, {
codeString: code.scannedCode,
codeSymbology: code.symbology,
boundingBox: code.boundingBox
}]);
});
}}
/>
// NEW - Save template to your storage
const saveTemplate = async () => {
const template: TemplateData = { id: `template_${Date.now()}`, templateCodes };
await AsyncStorage.setItem(`template_${template.id}`, JSON.stringify(template));
};
// NEW - Apply template via prop
<VisionCamera
template={activeTemplate} // Pass TemplateData object or null
// ... other props
/>
// NEW - Delete template from your storage
await AsyncStorage.removeItem(`template_${id}`);
# API Changes Summary
| Old API (v2.x - VisionSdkView) | New API (v3.0.0 - VisionCamera) | Notes |
|---|---|---|
createTemplate() method | Build in React Native | Collect barcodes via onBarcodeDetected |
getAllTemplates() | Removed | Manage retrieval from your storage |
deleteTemplateWithId(id) | Removed | Delete from your storage manually |
deleteAllTemplates() | Removed | Clear your storage manually |
onCreateTemplate event | Build template in state | Full control over UI/UX |
setObjectDetectionSettings({ selectedTemplate }) | <VisionCamera template={...} /> | Pass via template prop |
# Template Data Structure
interface TemplateData {
id: string; // Unique template identifier
templateCodes: TemplateCode[]; // Array of barcode patterns
}
interface TemplateCode {
codeString: string; // Barcode value (e.g., "1234567890")
codeSymbology: string; // Barcode type (e.g., "CODE_128", "QR_CODE")
boundingBox?: { // Optional position data
x: number;
y: number;
width: number;
height: number;
};
}
# Storage Options
Choose the storage solution that fits your needs:
| Storage | Use Case | Pros | Cons |
|---|---|---|---|
| AsyncStorage | Simple apps | Easy to use, built-in | Limited size, local only |
| Redux | State management apps | Centralized state | Requires persistence setup |
| SQLite | Complex apps | Structured queries | More setup required |
| MMKV | High performance | Very fast | Additional dependency |
| Remote API | Enterprise/Multi-device | Cloud sync | Requires backend |
# Best Practices
# 1. Use Consistent Storage Keys
const TEMPLATE_KEY_PREFIX = 'vision_template_';
// Save
await AsyncStorage.setItem(`${TEMPLATE_KEY_PREFIX}${template.id}`, JSON.stringify(template));
// Load all
const keys = (await AsyncStorage.getAllKeys())
.filter(key => key.startsWith(TEMPLATE_KEY_PREFIX));
# 2. Validate Template Data
const isValidTemplate = (data: any): data is TemplateData => {
return (
data &&
typeof data.id === 'string' &&
Array.isArray(data.templateCodes) &&
data.templateCodes.every(code =>
typeof code.codeString === 'string' &&
typeof code.codeSymbology === 'string'
)
);
};
if (!isValidTemplate(template)) {
throw new Error('Invalid template data');
}
# 3. Handle Storage Errors Gracefully
const saveTemplate = async (template: TemplateData) => {
try {
await AsyncStorage.setItem(`template_${template.id}`, JSON.stringify(template));
return { success: true };
} catch (error) {
if (error.code === 'STORAGE_FULL') {
Alert.alert('Storage Full', 'Please free up some space');
} else {
Alert.alert('Error', 'Failed to save template');
}
return { success: false, error };
}
};
# Troubleshooting
# Template Not Applied
Issue: Template doesn't seem to be working after applying.
Solution: Ensure you're passing a valid TemplateData object (not a JSON string):
// Correct - pass the object
<VisionCamera template={templateObject} />
// Wrong - don't pass a JSON string
<VisionCamera template={JSON.stringify(templateObject)} /> // Wrong
# Template Lost After App Restart
Issue: Templates disappear when app restarts.
Solution:
- Verify you're using persistent storage (AsyncStorage, not just state)
- Check that keys are correct when loading
- Use redux-persist or similar for state management
# Further Reading
- Props Documentation - Full VisionCamera props reference
- VisionCamera Guide - Complete VisionCamera documentation
- Multiple Barcode Scanning - Using templates with multi-scan
- AsyncStorage Documentation - Official AsyncStorage docs