1. React Native
  2. Template Management

React Native

Template Management

WARNING

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