Scanner APIAPIs
The Scanner API enables an extension to access scanner data and available scanning sources supported by the device.
Supporting targets
Anchor to scannerapiScannerApi
- Anchor to scannerDataSubscribablescannerDataSubscribableRemoteSubscribable<>required
Creates a subscription to scan events Provides an initial value and a callback to subscribe to value changes. Currently supports only one subscription. You can utilize
on a
to implement multiple subscriptions. Using
or the corresponding hooks counts as a subscription.
- Anchor to scannerSourcesSubscribablescannerSourcesSubscribableRemoteSubscribable<[]>required
Creates a subscription to the scanning sources available on the POS device. Provides an initial value and a callback to subscribe to value changes. Currently supports only one subscription. You can utilize
on a
to implement multiple subscriptions. Using
or the corresponding hooks counts as a subscription.
ScannerApiContent
- scannerDataSubscribable
Creates a subscription to scan events Provides an initial value and a callback to subscribe to value changes. Currently supports only one subscription. You can utilize `makeStatefulSubscribable` on a `RemoteSubscribable` to implement multiple subscriptions. Using `makeStatefulSubscribable` or the corresponding hooks counts as a subscription.
RemoteSubscribable<ScannerSubscriptionResult>
- scannerSourcesSubscribable
Creates a subscription to the scanning sources available on the POS device. Provides an initial value and a callback to subscribe to value changes. Currently supports only one subscription. You can utilize `makeStatefulSubscribable` on a `RemoteSubscribable` to implement multiple subscriptions. Using `makeStatefulSubscribable` or the corresponding hooks counts as a subscription.
RemoteSubscribable<ScannerSource[]>
export interface ScannerApiContent {
/** Creates a subscription to scan events
* Provides an initial value and a callback to subscribe to value changes. Currently supports only one subscription.
* You can utilize `makeStatefulSubscribable` on a `RemoteSubscribable` to implement multiple subscriptions.
* Using `makeStatefulSubscribable` or the corresponding hooks counts as a subscription.
*/
scannerDataSubscribable: RemoteSubscribable<ScannerSubscriptionResult>;
/** Creates a subscription to the scanning sources available on the POS device.
* Provides an initial value and a callback to subscribe to value changes. Currently supports only one subscription.
* You can utilize `makeStatefulSubscribable` on a `RemoteSubscribable` to implement multiple subscriptions.
* Using `makeStatefulSubscribable` or the corresponding hooks counts as a subscription.
*/
scannerSourcesSubscribable: RemoteSubscribable<ScannerSource[]>;
}
ScannerSubscriptionResult
- data
The string data from the last scanner event received.
string
- source
The scanning source from which the scan event came.
ScannerSource
export interface ScannerSubscriptionResult {
/** The string data from the last scanner event received. */
data?: string;
/** The scanning source from which the scan event came. */
source?: ScannerSource;
}
ScannerSource
The scanner source the POS device supports.
'camera' | 'external' | 'embedded'
Anchor to examplesExamples
Examples of receiving updates from the Scanner API
Anchor to example-subscribe-to-scan-event-updatesSubscribe to scan event updates
Anchor to example-receiving-updates-on-available-scanner-sourcesReceiving updates on available scanner sources
Subscribe to scan event updates
examples
Subscribe to scan event updates
React
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 ( <Navigator> <Screen name="Home" title="Home"> <Stack direction="horizontal"> <Text>{`Scanned data: ${data || ''} with ${source || ''}`}</Text> </Stack> </Screen> </Navigator> ); }; export default reactExtension('pos.home.modal.render', () => ( <SmartGridModal /> ));
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 || ''}`); }); });
Receiving updates on available scanner sources
React
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 ( <Navigator> <Screen name="Home" title="Home"> <Stack direction="horizontal"> <Text>{`Available scanner sources: ${scannerSources}`}</Text> </Stack> </Screen> </Navigator> ); }; export default reactExtension('pos.home.modal.render', () => ( <SmartGridModal /> ));
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}`); }); });
Anchor to example-use-casesUse cases
Anchor to example-hardware-scanner-exampleHardware scanner example
In this example, assuming a physical scanner is connected to the POS, any scans performed when ui extensions are in use will automatically add the product to the cart if the data exists on the shop.
Anchor to example-conditional-scanner-source-rendering-exampleConditional scanner source rendering example
There might be situations where a developer needs to conditionally render UI elements based on the available scanning sources of the device on which the extension is installed. For example, an extension could be designed for full-screen camera scanning, but a device like POS GO does not have a camera. In such cases, it would be necessary to avoid rendering the camera scanner component and instead create a UI that supports embedded scanning.
Hardware scanner example
examples
Hardware scanner example
description
In this example, assuming a physical scanner is connected to the POS, any scans performed when ui extensions are in use will automatically add the product to the cart if the data exists on the shop.
React
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 ( <Navigator> <Screen name="Home" title="Home"> <Stack direction="horizontal"> <Text>{`Scanned data: ${data || ''}`}</Text> </Stack> </Screen> </Navigator> ); }; export default reactExtension('pos.home.modal.render', () => ( <SmartGridModal /> ));
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 || ''}`); }); });
Conditional scanner source rendering example
description
There might be situations where a developer needs to conditionally render UI elements based on the available scanning sources of the device on which the extension is installed. For example, an extension could be designed for full-screen camera scanning, but a device like POS GO does not have a camera. In such cases, it would be necessary to avoid rendering the camera scanner component and instead create a UI that supports embedded scanning.
React
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 ( <Navigator> <Screen name="Home" title="Home"> <Stack direction="horizontal"> {hasCameraScanner ? ( <CameraScanner /> ) : ( <Stack direction="vertical" alignment="space-evenly"> <Text>{`Scanned data: ${data || ''}`}</Text> <Text>{`Scanned data source: ${source || ''}`}</Text> </Stack> )} </Stack> </Screen> </Navigator> ); }; export default reactExtension('pos.home.modal.render', () => ( <SmartGridModal /> ));
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); } }); });