--- title: Scanner API description: >- The Scanner API provides access to barcode and QR code scanning functionality on POS devices, allowing you to subscribe to scan events, monitor available scanner sources, and process scanned data through subscription callbacks. The API enables integration with device cameras, external scanners, and embedded scanning hardware. api_version: 2024-10 api_name: pos-ui-extensions source_url: html: >- https://shopify.dev/docs/api/pos-ui-extensions/2024-10/target-apis/platform-apis/scanner-api md: >- https://shopify.dev/docs/api/pos-ui-extensions/2024-10/target-apis/platform-apis/scanner-api.md --- # Scanner API The Scanner API provides access to barcode and QR code scanning functionality on POS devices, allowing you to subscribe to scan events, monitor available scanner sources, and process scanned data through subscription callbacks. The API enables integration with device cameras, external scanners, and embedded scanning hardware. #### Use cases * **Barcode scanning:** Implement barcode scanning for product lookup or inventory management. * **QR scanning:** Build QR code scanning features for customer engagement or loyalty programs. * **Custom workflows:** Create custom scanning workflows that process scan data. * **Real-time feedback:** Implement real-time scanning feedback with immediate processing. Support Targets (6) ### Supported targets * [pos.​customer-details.​action.​render](https://shopify.dev/docs/api/pos-ui-extensions/2024-10/targets/customer-details#customer-details-action-modal-) * [pos.​draft-order-details.​action.​render](https://shopify.dev/docs/api/pos-ui-extensions/2024-10/targets/draft-order-details#draft-order-details-action-modal-) * [pos.​home.​modal.​render](https://shopify.dev/docs/api/pos-ui-extensions/2024-10/targets/home-screen#home-screen-action-modal-) * [pos.​order-details.​action.​render](https://shopify.dev/docs/api/pos-ui-extensions/2024-10/targets/order-details#order-details-action-modal-) * [pos.​product-details.​action.​render](https://shopify.dev/docs/api/pos-ui-extensions/2024-10/targets/product-details#product-details-action-modal-) * [pos.​purchase.​post.​action.​render](https://shopify.dev/docs/api/pos-ui-extensions/2024-10/targets/post-purchase#post-purchase-action-modal-) ## ScannerApi The `ScannerApi` object provides access to scanning functionality and scanner source information. Access these properties through `api.scanner` to monitor scan events and available scanner sources. * scannerDataSubscribable RemoteSubscribable\ required Subscribe to scan events to receive barcode and QR code data when scanned. Supports one subscription at a time. Use for receiving real-time scan results. * scannerSourcesSubscribable RemoteSubscribable\ required Subscribe to changes in available scanner sources on the device. Supports one subscription at a time. Use to monitor which scanners are available (camera, external, or embedded). ### ScannerSubscriptionResult Represents the data from a scanner event. Contains the scanned string data and the hardware source that captured the scan. * data The string data from the last scanner event received. Contains the scanned barcode, QR code, or other scannable data. Returns \`undefined\` when no scan data is available. Use to process scanned content and implement scan-based business logic. ```ts string ``` * source The scanning source from which the scan event came. Returns one of the following scanner types: - \`'camera'\` - Device camera for scanning through the camera interface (available on most mobile POS devices) - \`'external'\` - External scanner hardware connected using USB, Bluetooth, or other methods (handheld scanners, dedicated devices) - \`'embedded'\` - Built-in scanning hardware integrated into the POS device (specialized terminals with integrated scanning) Returns \`undefined\` when no scan source is available. Use to implement source-specific logic or provide feedback about scanning method. ```ts ScannerSource ``` ```ts export interface ScannerSubscriptionResult { /** * The string data from the last scanner event received. Contains the scanned barcode, QR code, or other scannable data. Returns `undefined` when no scan data is available. Use to process scanned content and implement scan-based business logic. */ data?: string; /** * The scanning source from which the scan event came. Returns one of the following scanner types: * * - `'camera'` - Device camera for scanning through the camera interface (available on most mobile POS devices) * - `'external'` - External scanner hardware connected using USB, Bluetooth, or other methods (handheld scanners, dedicated devices) * - `'embedded'` - Built-in scanning hardware integrated into the POS device (specialized terminals with integrated scanning) * * Returns `undefined` when no scan source is available. Use to implement source-specific logic or provide feedback about scanning method. */ source?: ScannerSource; } ``` ### ScannerSource The scanner source the POS device supports. ```ts 'camera' | 'external' | 'embedded' ``` Examples ### Examples * #### Adapt UI to available scanner types ##### Description Adapt your scanning interface based on available hardware capabilities. This example shows how to conditionally render appropriate scanning UI for different device types—using camera scanning on devices with cameras while falling back to embedded scanning on devices like POS GO that lack camera hardware, ensuring a functional experience across all POS devices. ##### React ```tsx import React from 'react'; import { CameraScanner, Navigator, Screen, Stack, Text, useScannerDataSubscription, useScannerSourcesSubscription, reactExtension, } from '@shopify/ui-extensions-react/point-of-sale'; const SmartGridModal = () => { const {data, source} = useScannerDataSubscription(); const availableScanners = useScannerSourcesSubscription(); const hasCameraScanner = availableScanners.includes('camera'); return ( {hasCameraScanner ? ( ) : ( {`Scanned data: ${data || ''}`} {`Scanned data source: ${source || ''}`} )} ); }; export default reactExtension('pos.home.modal.render', () => ( )); ``` ##### TS ```ts import { CameraScanner, Navigator, Screen, Stack, Text, extension, } from '@shopify/ui-extensions/point-of-sale'; export default extension('pos.home.modal.render', (root, api) => { const dataText = root.createComponent('Text', null, 'Scanned data: '); const sourceText = root.createComponent( 'Text', null, 'Scanned data source: ', ); const cameraScanner = root.createComponent(CameraScanner); const stack1 = root.createComponent(Stack, { direction: 'horizontal', }); const stack2 = root.createComponent(Stack, { direction: 'vertical', alignment: 'space-evenly', }); const screen = root.createComponent(Screen, { title: 'Home', name: 'Home', }); const navigator = root.createComponent(Navigator); screen.append(stack1); navigator.append(screen); root.append(navigator); api.scanner.scannerDataSubscribable.subscribe((data, source) => { dataText.replaceChildren(`Scanned data: ${data || ''}`); sourceText.replaceChildren(`Scanned data source: ${source || ''}`); }); api.scanner.scannerSourcesSubscribable.subscribe((sources) => { // Clear previous children to avoid duplicate appending stack1.children = []; stack2.children = []; if (sources.include('camera')) { stack1.append(cameraScanner); } else { stack2.append(dataText); stack2.append(sourceText); stack1.append(stack2); } }); }); ``` * #### Add products from hardware scanner ##### Description Automatically add products to the cart when barcodes are scanned using external hardware scanners. This example listens for scan events and uses the scanned data to search for and add matching products, creating a seamless scanning workflow that works with physical scanners connected to the POS device. ##### React ```tsx import React from 'react'; import { Navigator, Screen, Stack, Text, useScannerDataSubscription, useApi, reactExtension, } from '@shopify/ui-extensions-react/point-of-sale'; const SmartGridModal = () => { const api = useApi<'pos.home.modal.render'>(); const {data} = useScannerDataSubscription(); return ( {`Scanned data: ${data || ''}`} ); }; export default reactExtension('pos.home.modal.render', () => ( )); ``` ##### TS ```ts import React from 'react'; import { Navigator, Screen, Stack, Text, extension, } from '@shopify/ui-extensions/point-of-sale'; export default extension('pos.home.modal.render', (root, api) => { const text = root.createComponent('Text', null, 'Scanned data: '); const stack = root.createComponent(Stack, { direction: 'horizontal', }); const screen = root.createComponent(Screen, { title: 'Home', name: 'Home', }); const navigator = root.createComponent(Navigator); stack.append(text); screen.append(stack); navigator.append(screen); root.append(navigator); api.scanner.scannerDataSubscribable.subscribe((data) => { text.replaceChildren(`Scanned data: ${data || ''}`); }); }); ``` * #### Monitor available scanner hardware ##### Description Subscribe to scanner source changes to detect which scanning methods are available on the device. This example demonstrates tracking available scanner sources (camera, external, embedded) in real time, allowing you to adapt your UI and functionality based on hardware capabilities. ##### React ```tsx import React from 'react'; import { Navigator, Screen, Stack, Text, useScannerSourcesSubscription, reactExtension, } from '@shopify/ui-extensions-react/point-of-sale'; const SmartGridModal = () => { const scannerSources = useScannerSourcesSubscription(); return ( {`Available scanner sources: ${scannerSources}`} ); }; export default reactExtension('pos.home.modal.render', () => ( )); ``` ##### TS ```ts import { Navigator, Screen, Stack, Text, extension, } from '@shopify/ui-extensions/point-of-sale'; export default extension('pos.home.modal.render', (root, api) => { const scannerSourcesText = root.createComponent( 'Text', null, 'Available scanner sources: ', ); const stack1 = root.createComponent(Stack, { direction: 'horizontal', }); const screen = root.createComponent(Screen, { title: 'Home', name: 'Home', }); const navigator = root.createComponent(Navigator); stack1.append(scannerSourcesText); screen.append(stack1); navigator.append(screen); root.append(navigator); api.scanner.scannerSourcesSubscribable.subscribe((sources) => { scannerSourcesText.replaceChildren(`Available scanner sources: ${sources}`); }); }); ``` * #### Subscribe to scan events ##### Description Subscribe to scan events to process barcode and QR code data as it arrives. This example shows how to listen for scans from any available scanner source (camera, external scanner, or embedded hardware), enabling you to add products to cart, look up information, or trigger custom workflows based on scanned codes. ##### React ```tsx import React from 'react'; import { Navigator, Screen, Stack, Text, useScannerDataSubscription, reactExtension, } from '@shopify/ui-extensions-react/point-of-sale'; const SmartGridModal = () => { const {data, source} = useScannerDataSubscription(); return ( {`Scanned data: ${data || ''} with ${source || ''}`} ); }; export default reactExtension('pos.home.modal.render', () => ( )); ``` ##### TS ```ts import { Navigator, Screen, Stack, Text, extension, } from '@shopify/ui-extensions/point-of-sale'; export default extension('pos.home.modal.render', (root, api) => { const dataText = root.createComponent('Text', null, 'Scanned data: '); const sourceText = root.createComponent( 'Text', null, 'Scanned data source: ', ); const stack1 = root.createComponent(Stack, { direction: 'horizontal', }); const screen = root.createComponent(Screen, { title: 'Home', name: 'Home', }); const navigator = root.createComponent(Navigator); stack1.append(dataText); stack1.append(sourceText); screen.append(stack1); navigator.append(screen); root.append(navigator); api.scanner.scannerDataSubscribable.subscribe((data, source) => { dataText.replaceChildren(`Scanned data: ${data || ''}`); sourceText.updateProps(`Scanned data source: ${source || ''}`); }); }); ``` ## Best practices * **Manage subscriptions carefully:** Remember that `RemoteSubscribable` supports only one subscription at a time. If you need multiple subscriptions, use `makeStatefulSubscribable` or manage subscription cleanup to avoid conflicts. * **Validate scanned data appropriately:** Validate scanned data before processing, implementing proper error handling for invalid codes, unsupported formats, or scanning errors. * **Provide clear scanning feedback:** Give users clear feedback about scan results, including success confirmations, error messages, and guidance when scans fail or produce invalid data. * **Adapt to available scanner sources:** Check available scanner sources and adapt your interface accordingly. ![](https://shopify.dev/images/templated-apis-screenshots/pos-ui-extensions/2024-10/camera-scanner-best-practice.png) ## Limitations * The Scanner API is only available in action (modal) targets where scanning functionality is supported and can't be used in other targets. * `RemoteSubscribable` supports only one subscription at a time. Use `makeStatefulSubscribable` if you need multiple components to subscribe to scan events simultaneously. * Scanning availability depends on device hardware capabilities and may vary between different POS devices and configurations.