A live camera view that takes pictures and scans QR codes and barcodes on iOS and Android
CameraView shows a live camera preview inside your layout, takes pictures
from it, and reports the QR codes and barcodes it sees. On iOS it is an
AVCaptureSession with photo and metadata outputs; on Android it is CameraX
with ML Kit barcode scanning. The same component is reachable as
One.iOS.CameraView and One.Android.CameraView, and picks the platform’s
camera itself.
Use it when the camera belongs inside your screen: a custom shutter, an overlay, scanning tickets or product codes. To let people take a photo in the system camera instead, use Image Picker.
Declare why you use the camera in your app config before prebuild. This
writes the iOS usage text and adds Android’s CAMERA permission:
import { one } from 'one/vite'
export default { plugins: [ one({ native: { app: { name: 'MyApp', imagePicker: { camera: 'Scan tickets at the door.' }, }, }, }), ],}Then ask for permission and turn the view on:
import { useState } from 'react'import { Button, Text, View } from 'react-native'import { One } from 'one'
function TicketScanner({ onTicket }: { onTicket: (code: string) => void }) { const [active, setActive] = useState(false) return ( <View style={{ flex: 1 }}> <One.iOS.CameraView style={{ flex: 1 }} active={active} codeTypes={['qr']} onCodeScanned={({ data }) => { setActive(false) onTicket(data) }} /> <Button title="Scan" onPress={async () => { const permission = await One.ImagePicker.requestCameraPermissions() if (permission.granted) setActive(true) }} /> </View> )}The view never asks for permission itself, so the button asks first and turns scanning on only after a yes.
To take a picture, hold a ref and call takePicture once the view reports
ready:
import { useRef, useState } from 'react'import { Button, Image, View } from 'react-native'import { One, type CameraViewRef } from 'one'
function ProfilePhoto() { const camera = useRef<CameraViewRef>(null) const [ready, setReady] = useState(false) const [photo, setPhoto] = useState<string>() return ( <View style={{ flex: 1 }}> <One.iOS.CameraView ref={camera} style={{ flex: 1 }} facing="front" codeTypes={[]} onStateChange={(state) => setReady(state === 'ready')} /> <Button title="Take photo" disabled={!ready} onPress={async () => { const picture = await camera.current?.takePicture({ quality: 0.8 }) if (picture) setPhoto(picture.uri) }} /> {photo ? <Image source={{ uri: photo }} style={{ width: 96, height: 96 }} /> : null} </View> )}Starting. The camera runs only while active is true, the view is on
screen, and the app is in the foreground. When all three hold and
permission is granted, the view starts a session on the wide-angle camera
facing the way facing says, fills its bounds with the preview, cropping
to keep the aspect ratio, and reports ready.
Scanning. The requested codeTypes are matched against what the camera
can detect. On iOS the metadata output reports each code in every frame it
appears in. On Android, ML Kit analyzes the newest frame at a time, around
1280 by 720, and skips a code identical to the last one delivered. Turning
the view off and on again starts fresh, so the same code is delivered again.
Taking pictures. takePicture captures a JPEG from the running
session, writes it to the app’s cache directory under Camera/, and
resolves with its file URI and upright size. The image is turned upright
for the way the phone is held. flash applies to each picture; quality
runs from 0 to 1 and sets JPEG compression. Ask for base64 only when you
need the bytes in JavaScript, since it copies the whole image across.
Torch and zoom. enableTorch keeps the light on while the camera runs.
zoom runs from 0, no zoom, to 1, the camera’s maximum. Changing flash,
enableTorch, or zoom adjusts the running camera without restarting it.
Changing props. A change to active, facing, or codeTypes restarts
the session with the new settings. Duplicate code types are removed before
they reach native code, and an unknown one throws.
Stopping. Setting active to false, unmounting, or leaving the window
releases the camera. On iOS, going to the background releases it too, and
the view starts again when the app becomes active, which is also when it
notices a permission granted in the system prompt or Settings. On Android,
CameraX follows the activity’s lifecycle, pausing in the background and
resuming in the foreground.
Interruptions on iOS. When another app or a call takes the camera, the
view reports inactive and restarts once the interruption ends. If the
media services reset, it restarts; any other runtime error reports failed.
| CameraViewProps | Type | Description |
|---|---|---|
activeboolean = trueRun the camera and scanning. | boolean = true | Run the camera and scanning. |
facingCameraFacing = "back"back or front. | CameraFacing = "back" | back or front. |
codeTypesreadonly CameraCodeType[] = ["qr"]The codes to scan. Empty shows the preview only. | readonly CameraCodeType[] = ["qr"] | The codes to scan. Empty shows the preview only. |
flashCameraFlashMode = "off"Flash for each picture: off, on, or auto. | CameraFlashMode = "off" | Flash for each picture: off, on, or auto. |
enableTorchboolean = falseKeep the light on while the camera runs. | boolean = false | Keep the light on while the camera runs. |
zoomnumber = 0From 0, no zoom, to 1, the maximum. | number = 0 | From 0, no zoom, to 1, the maximum. |
onStateChange(state: CameraState) => voidCalled when the camera state changes. | (state: CameraState) => void | Called when the camera state changes. |
onCodeScanned(code: CameraCode) => voidCalled with each code found. | (code: CameraCode) => void | Called with each code found. |
| CameraCode | Type | Description |
|---|---|---|
typeCameraCodeTypeWhich kind of code it is. | CameraCodeType | Which kind of code it is. |
datastringThe decoded text. | string | The decoded text. |
| CameraViewRef | Type | Description |
|---|---|---|
takePicture(options?: CameraPictureOptions) => Promise<CameraCapturedPicture>Take a picture from the running camera. | (options?: CameraPictureOptions) => Promise<CameraCapturedPicture> | Take a picture from the running camera. |
| CameraPictureOptions | Type | Description |
|---|---|---|
qualitynumber = 1JPEG quality from 0 to 1. | number = 1 | JPEG quality from 0 to 1. |
base64boolean = falseAlso return the image as base64. | boolean = false | Also return the image as base64. |
| CameraCapturedPicture | Type | Description |
|---|---|---|
uristringA file URI in the cache directory. | string | A file URI in the cache directory. |
widthnumberUpright width in pixels. | number | Upright width in pixels. |
heightnumberUpright height in pixels. | number | Upright height in pixels. |
format'jpg'The image format. | 'jpg' | The image format. |
base64stringThe JPEG as base64, when asked for. | string | The JPEG as base64, when asked for. |
takePicture rejects with a code:
| Code | Means |
|---|---|
| E_CAMERA_NOT_READY | The view is not ready: off, no permission, or no camera |
| E_CAMERA_INACTIVE | The camera stopped or restarted before the picture finished |
| E_CAMERA_CAPTURE | The camera failed to produce an image |
| E_CAMERA_UNMOUNTED | The view unmounted first |
CameraCodeType is qr, ean13, ean8, upce, code128, code39,
code93, pdf417, aztec, or dataMatrix. The view also takes every
React Native view prop, such as style.
| State | Means |
|---|---|
| inactive | Off, off screen, or interrupted |
| permission-required | Camera permission is not granted |
| unavailable | No camera faces that way |
| ready | Previewing, scanning, and able to take pictures |
| unsupported | None of the code types can be read |
| failed | The camera could not start |
| iOS | Android | Web | |
|---|---|---|---|
| Engine | AVFoundation | CameraX and ML Kit | Throws |
| Picture encoding | Camera JPEG as captured | Decoded, turned upright, re-encoded | |
| Repeated code | Every frame | Once in a row | |
| In background | Stops, then restarts | Paused | |
| Permission granted later | Notices on return | Needs a new start | |
| First state when off | Inactive | None |
active to false in onCodeScanned, as the usage example
does, before acting on the code.permission-required, set active
to false and back to true once permission is granted.permission-required and
shows nothing until permission is granted. getCameraPermissions reads
the status without prompting.CameraView requires a native build, so keep it in a native route or behind One.platform.unavailable,
and takePicture rejects with E_CAMERA_NOT_READY. Test scanning and
pictures on a device or an Android emulator with an emulated camera.import { useState } from 'react'import { Text, View } from 'react-native'import { One } from 'one'
type CameraState = 'inactive' | 'permission-required' | 'unavailable' | 'ready' | 'unsupported' | 'failed'
const messages: Record<CameraState, string> = { inactive: 'Camera off', 'permission-required': 'Allow camera access in Settings', unavailable: 'No camera found', ready: 'Point at a code', unsupported: 'This camera cannot read these codes', failed: 'The camera could not start',}
function ScannerWithStatus({ onCode }: { onCode: (data: string) => void }) { const [state, setState] = useState<CameraState>('inactive') return ( <View style={{ flex: 1 }}> <One.Android.CameraView style={{ flex: 1 }} onStateChange={setState} onCodeScanned={({ data }) => onCode(data)} /> <Text>{messages[state]}</Text> </View> )}Every state gets a message, so people always know why they see no preview.
One.Android.CameraView and One.iOS.CameraView are the same component, so
this runs on both platforms.
import { useState } from 'react'import { Button, View } from 'react-native'import { One } from 'one'
function ProductScanner({ onProduct }: { onProduct: (code: string) => void }) { const [facing, setFacing] = useState<'back' | 'front'>('back') return ( <View style={{ flex: 1 }}> <One.iOS.CameraView style={{ flex: 1 }} facing={facing} codeTypes={['ean13', 'ean8', 'upce']} onCodeScanned={({ type, data }) => onProduct(`${type}:${data}`)} /> <Button title="Flip camera" onPress={() => setFacing(facing === 'back' ? 'front' : 'back')} /> </View> )}Limiting codeTypes to retail barcodes keeps the scanner from firing on
unrelated QR codes nearby. Flipping facing restarts the session on the
other camera.
Edit this page on GitHub.