CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-expo-camera

A React component that renders a camera preview for React Native apps with photo/video capture, barcode scanning, and cross-platform support.

Pending
Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Pending

The risk profile of this skill

Overview
Eval results
Files

barcode-scanning.mddocs/

Barcode Scanning

Expo Camera provides comprehensive barcode and QR code scanning capabilities supporting multiple barcode formats with real-time detection during camera preview and static image scanning from URLs.

Capabilities

Real-time Barcode Scanning

Configure the camera component to detect barcodes in real-time during camera preview.

/**
 * Configure barcode scanning settings for the camera
 */
interface BarcodeSettings {
  /**
   * Array of barcode types to detect
   */
  barcodeTypes: BarcodeType[];
}

/**
 * Callback for barcode detection events
 * @param result The detected barcode information
 */
type BarcodeScanningCallback = (result: BarcodeScanningResult) => void;

Static Image Barcode Scanning

Scan barcodes from static images via URL.

/**
 * Scan barcodes from an image at the given URL
 * @param url URL of the image to scan
 * @param barcodeTypes Array of barcode types to detect (defaults to ['qr'])
 * @returns Promise resolving to array of detected barcodes
 */
function scanFromURLAsync(
  url: string,
  barcodeTypes?: BarcodeType[]
): Promise<BarcodeScanningResult[]>;

Modern Scanner (iOS Only)

Native iOS barcode scanner with advanced features.

/**
 * Launch the modern iOS barcode scanner
 * @returns Promise resolving when scanner is launched
 */
launchModernScanner(): Promise<void>;

/**
 * Launch the barcode scanner with configuration options (iOS only)
 * @param options Scanning configuration options
 * @returns Promise resolving when scanner launches
 */
launchScanner(options?: ScanningOptions): Promise<void>;

/**
 * Dismiss the active barcode scanner (iOS only)
 * @returns Promise resolving when scanner is dismissed
 */
dismissScanner(): Promise<void>;

/**
 * Configuration options for iOS barcode scanner
 */
interface ScanningOptions {
  /**
   * Types of barcodes to scan for
   */
  barcodeTypes: BarcodeType[];
  
  /**
   * Enable pinch-to-zoom gesture
   * @default true
   */
  isPinchToZoomEnabled?: boolean;
  
  /**
   * Show guidance text over video
   * @default true
   */
  isGuidanceEnabled?: boolean;
  
  /**
   * Highlight recognized items
   * @default false
   */
  isHighlightingEnabled?: boolean;
}

Barcode Result Structure

Complete structure of barcode scanning results.

interface BarcodeScanningResult {
  /**
   * Detected barcode type
   */
  type: string;
  
  /**
   * Parsed barcode data
   */
  data: string;
  
  /**
   * Raw barcode data (Android only)
   */
  raw?: string;
  
  /**
   * Corner points of the barcode bounding box
   * Order varies by platform:
   * - Android: topLeft, topRight, bottomRight, bottomLeft
   * - iOS: bottomLeft, bottomRight, topLeft, topRight  
   * - Web: topLeft, bottomLeft, topRight, bottomRight
   */
  cornerPoints: BarcodePoint[];
  
  /**
   * Bounding box of the detected barcode
   */
  bounds: BarcodeBounds;
  
  /**
   * Platform-specific extra information (Android only)
   */
  extra?: AndroidBarcode;
}

interface BarcodePoint {
  x: number;
  y: number;
}

interface BarcodeBounds {
  /**
   * Origin point of bounding box
   */
  origin: BarcodePoint;
  
  /**
   * Size of bounding box
   */
  size: BarcodeSize;
}

interface BarcodeSize {
  width: number;
  height: number;
}

Supported Barcode Types

All supported barcode formats across platforms.

type BarcodeType =
  | 'aztec'
  | 'ean13'
  | 'ean8'
  | 'qr'
  | 'pdf417'
  | 'upc_e'
  | 'datamatrix'
  | 'code39'
  | 'code93'
  | 'itf14'
  | 'codabar'
  | 'code128'
  | 'upc_a';

Android Enhanced Barcode Data

Extended barcode information available on Android platform.

/**
 * Enhanced barcode information for Android platform
 */
type AndroidBarcode =
  | ContactInfoBarcode
  | GeoPointBarcode
  | SmsBarcode
  | UrlBarcode
  | CalendarEventBarcode
  | DriverLicenseBarcode
  | EmailBarcode
  | PhoneBarcode
  | WifiBarcode;

interface ContactInfoBarcode {
  type: 'contactInfo';
  firstName?: string;
  middleName?: string;
  lastName?: string;
  title?: string;
  organization?: string;
  email?: string;
  phone?: string;
  url?: string;
  address?: string;
}

interface GeoPointBarcode {
  type: 'geoPoint';
  lat: string;
  lng: string;
}

interface SmsBarcode {
  type: 'sms';
  phoneNumber?: string;
  message?: string;
}

interface UrlBarcode {
  type: 'url';
  url?: string;
}

interface CalendarEventBarcode {
  type: 'calendarEvent';
  summary?: string;
  description?: string;
  location?: string;
  start?: string;
  end?: string;
}

interface DriverLicenseBarcode {
  type: 'driverLicense';
  firstName?: string;
  middleName?: string;
  lastName?: string;
  licenseNumber?: string;
  expiryDate?: string;
  issueDate?: string;
  addressStreet?: string;
  addressCity?: string;
  addressState?: string;
}

interface EmailBarcode {
  type: 'email';
  address?: string;
  subject?: string;
  body?: string;
}

interface PhoneBarcode {
  type: 'phone';
  number?: string;
  phoneNumberType?: string;
}

interface WifiBarcode {
  type: 'wifi';
  ssid?: string;
  password?: string;
  encryptionType?: string;
}

Usage Examples

Basic QR Code Scanning

import React, { useState } from 'react';
import { View, Text, StyleSheet, Alert } from 'react-native';
import { CameraView, BarcodeScanningResult } from 'expo-camera';

export function QRCodeScanner() {
  const [scannedData, setScannedData] = useState<string | null>(null);

  const handleBarcodeScanned = (result: BarcodeScanningResult) => {
    console.log('Barcode scanned:', result);
    setScannedData(result.data);
    
    Alert.alert(
      'QR Code Detected',
      result.data,
      [
        { text: 'OK', onPress: () => setScannedData(null) }
      ]
    );
  };

  return (
    <View style={styles.container}>
      <CameraView
        style={styles.camera}
        facing="back"
        onBarcodeScanned={handleBarcodeScanned}
        barcodeScannerSettings={{
          barcodeTypes: ['qr']
        }}
      />
      
      {scannedData && (
        <View style={styles.resultContainer}>
          <Text style={styles.resultText}>
            Last scanned: {scannedData}
          </Text>
        </View>
      )}
    </View>
  );
}

const styles = StyleSheet.create({
  container: { flex: 1 },
  camera: { flex: 1 },
  resultContainer: {
    position: 'absolute',
    bottom: 100,
    left: 20,
    right: 20,
    backgroundColor: 'rgba(0,0,0,0.8)',
    padding: 15,
    borderRadius: 10,
  },
  resultText: {
    color: 'white',
    textAlign: 'center',
  },
});

Multi-Format Barcode Scanner

import React, { useState } from 'react';
import { View, Text, StyleSheet, FlatList } from 'react-native';
import { CameraView, BarcodeScanningResult, BarcodeType } from 'expo-camera';

interface ScannedBarcode {
  id: string;
  type: string;
  data: string;
  timestamp: Date;
}

export function MultiFormatBarcodeScanner() {
  const [scannedBarcodes, setScannedBarcodes] = useState<ScannedBarcode[]>([]);

  const supportedTypes: BarcodeType[] = [
    'qr',
    'ean13',
    'ean8',
    'code128',
    'code39',
    'upc_a',
    'upc_e',
    'pdf417',
    'aztec',
    'datamatrix'
  ];

  const handleBarcodeScanned = (result: BarcodeScanningResult) => {
    // Avoid duplicate scans of the same barcode
    const isDuplicate = scannedBarcodes.some(
      barcode => barcode.data === result.data && barcode.type === result.type
    );

    if (!isDuplicate) {
      const newBarcode: ScannedBarcode = {
        id: `${Date.now()}-${result.type}`,
        type: result.type,
        data: result.data,
        timestamp: new Date(),
      };

      setScannedBarcodes(prev => [newBarcode, ...prev].slice(0, 10)); // Keep last 10
      
      console.log('New barcode detected:', {
        type: result.type,
        data: result.data,
        bounds: result.bounds,
        cornerPoints: result.cornerPoints,
      });
    }
  };

  const renderBarcodeItem = ({ item }: { item: ScannedBarcode }) => (
    <View style={styles.barcodeItem}>
      <Text style={styles.barcodeType}>{item.type.toUpperCase()}</Text>
      <Text style={styles.barcodeData}>{item.data}</Text>
      <Text style={styles.barcodeTime}>
        {item.timestamp.toLocaleTimeString()}
      </Text>
    </View>
  );

  return (
    <View style={styles.container}>
      <CameraView
        style={styles.camera}
        facing="back"
        onBarcodeScanned={handleBarcodeScanned}
        barcodeScannerSettings={{
          barcodeTypes: supportedTypes
        }}
      />
      
      <View style={styles.overlay}>
        <Text style={styles.instructions}>
          Point camera at barcode or QR code
        </Text>
        <Text style={styles.supportedFormats}>
          Supports: QR, EAN, UPC, Code128, Code39, PDF417, Aztec, DataMatrix
        </Text>
      </View>

      {scannedBarcodes.length > 0 && (
        <View style={styles.resultsContainer}>
          <Text style={styles.resultsTitle}>Recent Scans:</Text>
          <FlatList
            data={scannedBarcodes}
            renderItem={renderBarcodeItem}
            keyExtractor={item => item.id}
            style={styles.resultsList}
          />
        </View>
      )}
    </View>
  );
}

const styles = StyleSheet.create({
  container: { flex: 1 },
  camera: { flex: 1 },
  overlay: {
    position: 'absolute',
    top: 100,
    left: 20,
    right: 20,
    alignItems: 'center',
  },
  instructions: {
    color: 'white',
    fontSize: 18,
    fontWeight: 'bold',
    textAlign: 'center',
    backgroundColor: 'rgba(0,0,0,0.7)',
    padding: 10,
    borderRadius: 10,
  },
  supportedFormats: {
    color: 'white',
    fontSize: 12,
    textAlign: 'center',
    backgroundColor: 'rgba(0,0,0,0.5)',
    padding: 5,
    borderRadius: 5,
    marginTop: 10,
  },
  resultsContainer: {
    position: 'absolute',
    bottom: 0,
    left: 0,
    right: 0,
    backgroundColor: 'rgba(255,255,255,0.95)',
    maxHeight: 200,
  },
  resultsTitle: {
    fontSize: 16,
    fontWeight: 'bold',
    padding: 10,
    textAlign: 'center',
  },
  resultsList: {
    maxHeight: 150,
  },
  barcodeItem: {
    padding: 10,
    borderBottomWidth: 1,
    borderBottomColor: '#eee',
  },
  barcodeType: {
    fontSize: 12,
    fontWeight: 'bold',
    color: '#007AFF',
  },
  barcodeData: {
    fontSize: 14,
    marginVertical: 2,
  },
  barcodeTime: {
    fontSize: 10,
    color: '#666',
  },
});

Static Image Scanning

import React, { useState } from 'react';
import { View, TextInput, TouchableOpacity, Text, StyleSheet, Alert } from 'react-native';
import { scanFromURLAsync, BarcodeScanningResult, BarcodeType } from 'expo-camera';

export function StaticImageScanner() {
  const [imageUrl, setImageUrl] = useState('');
  const [isScanning, setIsScanning] = useState(false);
  const [results, setResults] = useState<BarcodeScanningResult[]>([]);

  const scanImageURL = async () => {
    if (!imageUrl.trim()) {
      Alert.alert('Error', 'Please enter an image URL');
      return;
    }

    setIsScanning(true);
    try {
      const barcodeTypes: BarcodeType[] = [
        'qr', 'ean13', 'ean8', 'code128', 'code39', 'upc_a', 'upc_e'
      ];
      
      const detectedBarcodes = await scanFromURLAsync(imageUrl, barcodeTypes);
      
      setResults(detectedBarcodes);
      
      if (detectedBarcodes.length === 0) {
        Alert.alert('No Barcodes Found', 'No barcodes were detected in the image');
      } else {
        Alert.alert(
          'Barcodes Found', 
          `Found ${detectedBarcodes.length} barcode(s)`
        );
      }
    } catch (error) {
      Alert.alert('Scan Failed', `Error scanning image: ${error.message}`);
    } finally {
      setIsScanning(false);
    }
  };

  const scanQROnly = async () => {
    if (!imageUrl.trim()) return;

    setIsScanning(true);
    try {
      // Scan for QR codes only (iOS limitation note)
      const qrCodes = await scanFromURLAsync(imageUrl, ['qr']);
      setResults(qrCodes);
      
      if (qrCodes.length > 0) {
        Alert.alert('QR Code Found', qrCodes[0].data);
      } else {
        Alert.alert('No QR Code Found', 'No QR codes detected in the image');
      }
    } catch (error) {
      Alert.alert('Scan Failed', error.message);
    } finally {
      setIsScanning(false);
    }
  };

  return (
    <View style={styles.container}>
      <Text style={styles.title}>Static Image Barcode Scanner</Text>
      
      <TextInput
        style={styles.input}
        placeholder="Enter image URL"
        value={imageUrl}
        onChangeText={setImageUrl}
        multiline
      />
      
      <View style={styles.buttonContainer}>
        <TouchableOpacity
          style={[styles.button, isScanning && styles.disabledButton]}
          onPress={scanImageURL}
          disabled={isScanning}
        >
          <Text style={styles.buttonText}>
            {isScanning ? 'Scanning...' : 'Scan All Types'}
          </Text>
        </TouchableOpacity>
        
        <TouchableOpacity
          style={[styles.button, isScanning && styles.disabledButton]}
          onPress={scanQROnly}
          disabled={isScanning}
        >
          <Text style={styles.buttonText}>
            {isScanning ? 'Scanning...' : 'Scan QR Only'}
          </Text>
        </TouchableOpacity>
      </View>

      {results.length > 0 && (
        <View style={styles.resultsContainer}>
          <Text style={styles.resultsTitle}>Scan Results:</Text>
          {results.map((result, index) => (
            <View key={index} style={styles.resultItem}>
              <Text style={styles.resultType}>Type: {result.type}</Text>
              <Text style={styles.resultData}>Data: {result.data}</Text>
              {result.bounds && (
                <Text style={styles.resultBounds}>
                  Bounds: {result.bounds.size.width} x {result.bounds.size.height}
                </Text>
              )}
            </View>
          ))}
        </View>
      )}

      <Text style={styles.note}>
        Note: On iOS, only QR codes are supported for URL scanning
      </Text>
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    padding: 20,
    backgroundColor: '#f5f5f5',
  },
  title: {
    fontSize: 20,
    fontWeight: 'bold',
    textAlign: 'center',
    marginBottom: 20,
  },
  input: {
    borderWidth: 1,
    borderColor: '#ddd',
    borderRadius: 8,
    padding: 15,
    backgroundColor: 'white',
    marginBottom: 20,
    minHeight: 100,
    textAlignVertical: 'top',
  },
  buttonContainer: {
    flexDirection: 'row',
    justifyContent: 'space-between',
    marginBottom: 20,
  },
  button: {
    backgroundColor: '#007AFF',
    padding: 15,
    borderRadius: 8,
    flex: 0.48,
  },
  disabledButton: {
    backgroundColor: '#ccc',
  },
  buttonText: {
    color: 'white',
    textAlign: 'center',
    fontWeight: 'bold',
  },
  resultsContainer: {
    backgroundColor: 'white',
    borderRadius: 8,
    padding: 15,
    marginBottom: 20,
  },
  resultsTitle: {
    fontSize: 16,
    fontWeight: 'bold',
    marginBottom: 10,
  },
  resultItem: {
    marginBottom: 15,
    paddingBottom: 15,
    borderBottomWidth: 1,
    borderBottomColor: '#eee',
  },
  resultType: {
    fontWeight: 'bold',
    color: '#007AFF',
  },
  resultData: {
    marginTop: 5,
  },
  resultBounds: {
    marginTop: 5,
    fontSize: 12,
    color: '#666',
  },
  note: {
    fontSize: 12,
    color: '#666',
    textAlign: 'center',
    fontStyle: 'italic',
  },
});

iOS Modern Scanner

import React, { useEffect } from 'react';
import { View, TouchableOpacity, Text, StyleSheet, Platform } from 'react-native';
import { CameraView, ScanningOptions, ScanningResult } from 'expo-camera';

export function ModernBarcodeScanner() {
  const cameraRef = React.useRef<CameraView>(null);

  // Listen for modern scanner results
  useEffect(() => {
    if (Platform.OS !== 'ios') return;

    // Set up event listener for modern barcode scanning
    const subscription = CameraManager.addListener(
      'onModernBarcodeScanned',
      (event: ScanningResult) => {
        console.log('Modern scanner result:', event);
        // Handle the scanned result
      }
    );

    return () => subscription?.remove();
  }, []);

  const launchModernScanner = async () => {
    if (Platform.OS !== 'ios') {
      console.warn('Modern scanner is only available on iOS');
      return;
    }

    if (!cameraRef.current) return;

    try {
      await cameraRef.current.launchModernScanner();
    } catch (error) {
      console.error('Failed to launch modern scanner:', error);
    }
  };

  const launchScannerWithOptions = async () => {
    if (Platform.OS !== 'ios') return;

    try {
      const options: ScanningOptions = {
        barcodeTypes: ['qr', 'ean13', 'code128'],
        isPinchToZoomEnabled: true,
        isGuidanceEnabled: true,
        isHighlightingEnabled: true,
      };

      // Note: This would typically be called on a CameraManager instance
      await CameraManager.launchScanner(options);
    } catch (error) {
      console.error('Failed to launch scanner with options:', error);
    }
  };

  const dismissScanner = async () => {
    if (Platform.OS !== 'ios') return;

    try {
      await CameraManager.dismissScanner();
    } catch (error) {
      console.error('Failed to dismiss scanner:', error);
    }
  };

  if (Platform.OS !== 'ios') {
    return (
      <View style={styles.container}>
        <Text style={styles.unavailable}>
          Modern scanner is only available on iOS
        </Text>
      </View>
    );
  }

  return (
    <View style={styles.container}>
      <CameraView
        ref={cameraRef}
        style={styles.camera}
        facing="back"
      />
      
      <View style={styles.controls}>
        <TouchableOpacity style={styles.button} onPress={launchModernScanner}>
          <Text style={styles.buttonText}>Launch Modern Scanner</Text>
        </TouchableOpacity>
        
        <TouchableOpacity style={styles.button} onPress={launchScannerWithOptions}>
          <Text style={styles.buttonText}>Launch with Options</Text>
        </TouchableOpacity>
        
        <TouchableOpacity style={styles.button} onPress={dismissScanner}>
          <Text style={styles.buttonText}>Dismiss Scanner</Text>
        </TouchableOpacity>
      </View>
    </View>
  );
}

const styles = StyleSheet.create({
  container: { flex: 1 },
  camera: { flex: 1 },
  unavailable: {
    textAlign: 'center',
    fontSize: 16,
    color: '#666',
    marginTop: 100,
  },
  controls: {
    position: 'absolute',
    bottom: 50,
    left: 20,
    right: 20,
  },
  button: {
    backgroundColor: '#007AFF',
    padding: 15,
    borderRadius: 8,
    marginBottom: 10,
  },
  buttonText: {
    color: 'white',
    textAlign: 'center',
    fontWeight: 'bold',
  },
});

docs

barcode-scanning.md

camera-component.md

index.md

media-capture.md

permissions.md

tile.json