Skip to main content

<Camera> — full prop reference

Every prop is optional. <Camera> works with no props — it captures, and you wire onCapture to receive the result. The props fall into the groups below, followed by deep-dives on panorama guidance, the JSON settings objects, the guidanceCopy keys, and the allowed enum values.

Defaults are the resolved runtime values

The defaults in these tables are the values the component actually resolves at mount — cross-checked against the component destructure and DEFAULT_PANORAMA_SETTINGS / DEFAULT_FLOW_GATE_SETTINGS — not the prop JSDoc, which is stale in a few places. Where a JSDoc claim disagrees, the table here is authoritative.

Capture source & lens

Uncontrolled — read once at mount.

PropTypeDefaultDescription
defaultCaptureSourceCaptureSource ('ar' | 'non-ar')'non-ar'Initial capture source, read once at mount (uncontrolled). Clamped by captureSources: 'ar' forces it on, 'non-ar' forces it off, 'both' uses this value.
defaultLensCameraLens ('1x' | '0.5x')'1x'Initial physical lens. When captureSources='ar' the lens is forced to '1x' — the ultra-wide isn't usable in AR.
captureSourcesCaptureSourcesMode ('ar' | 'non-ar' | 'both')'both'Which capture sources the host allows. 'both' shows the AR toggle; 'ar' is AR-only (toggle and 0.5× chooser hidden); 'non-ar' hides the toggle. A single source overrides a conflicting defaultCaptureSource.
engine'batch-keyframe''batch-keyframe'Which stitcher engine to drive. Only 'batch-keyframe' is supported and is the default.

See Flash & lenses for how lens selection, device capability, and flash interact.

Stitcher tunables

Uncontrolled internal-tester knobs; most apps never set these. Each flat default* prop seeds the matching field in the settings tree at mount. v0.16 adds the stitcher and frameSelection JSON-object props, which take precedence over the flat props below.

PropTypeDefaultDescription
defaultStitchModeStitchMode ('auto' | 'panorama' | 'scans')'auto'Initial cv::Stitcher pipeline mode seed. Resolves through DEFAULT_PANORAMA_SETTINGS.stitcher.stitchMode.
defaultBlenderBlender ('multiband' | 'feather')'multiband'Initial pixel blender seed (DEFAULT_PANORAMA_SETTINGS.stitcher.blenderType).
defaultSeamFinderSeamFinder ('graphcut' | 'skip')'graphcut'Initial seam-finder strategy seed (DEFAULT_PANORAMA_SETTINGS.stitcher.seamFinderType).
defaultWarperWarper ('plane' | 'cylindrical' | 'spherical')'plane'Initial output projection seed (PANORAMA mode only; SCANS hard-wires plane). Resolves to DEFAULT_PANORAMA_SETTINGS.stitcher.warperType — v0.16 reverted this from 'spherical' back to 'plane'.
maxInscribedRectCropbooleanfalseCrop-strategy seed. false keeps the non-black bounding rect; true crops to the max inscribed rectangle (no black corners, more CPU). Forced off internally when rectCrop is on. Maps to stitcher.enableMaxInscribedRectCrop. See Inscribed-rect crop.
defaultFlowNoveltyPercentilenumber0.85Flow-gate novelty percentile seed (flow-based mode). DEFAULT_FLOW_GATE_SETTINGS.noveltyPercentile. Clamped natively to [0.50, 0.99].
defaultFlowEvalEveryNFramesnumber5Flow-gate eval throttle seed. DEFAULT_FLOW_GATE_SETTINGS.evalEveryNFrames. Clamped natively to [1, 10].
defaultFlowMaxTranslationCmnumber50Flow-gate translation budget (cm) seed. DEFAULT_FLOW_GATE_SETTINGS.maxTranslationCm. 0 disables; clamped natively to [0, 100].
defaultKeyframeMaxCountnumber6Hard cap on accepted keyframes seed. Resolves to DEFAULT_PANORAMA_SETTINGS.frameSelection.maxKeyframes. Clamped natively to [3, 10]. Maps to frameSelection.maxKeyframes.
defaultKeyframeOverlapThresholdnumber0.20Required new-content fraction seed. Resolves to DEFAULT_PANORAMA_SETTINGS.frameSelection.overlapThreshold = 0.20 (the FrameSelectionSettings docstring's 0.15 is stale). Clamped natively to [0.10, 0.80].
defaultMaxKeyframeIntervalMsnumber1500Time-budget force-accept (ms) for the keyframe gate seed. Resolves to DEFAULT_PANORAMA_SETTINGS.frameSelection.maxKeyframeIntervalMs = 1500 (the prop JSDoc's 2000 is stale). 0 disables. Applies to AR + non-AR.
defaultCompositingResolMPnumberForward-looking; accepted for API stability but currently a no-op. Will wire to cv::Stitcher compositing resolution later.
defaultRegistrationResolMPnumberForward-looking; accepted but currently a no-op.
defaultSeamEstimationResolMPnumberForward-looking; accepted but currently a no-op.
stitcherPartial<BatchStitcherSettings>v0.16 JSON-object form of the stitcher config (warperType / blenderType / seamFinderType / stitchMode / enableMaxInscribedRectCrop). Any field set here wins over the matching flat default* prop. See Settings JSON objects.
frameSelectionPartial<FrameSelectionSettings>v0.16 JSON-object form of the frame-gate config (mode / maxKeyframes / overlapThreshold / maxKeyframeIntervalMs / flow). flow is deep-merged. Wins over the flat default* props. See Settings JSON objects.
Runtime gear panel

The in-app settings modal (gear, shown via showSettingsButton) can edit the stitcher and frame-gate fields at runtime; those edits override these seed props at capture time.

Inscribed-rect crop

When cv::Stitcher warps the keyframes onto the output canvas, the filled region is rarely a perfect rectangle — the edges curve and the corners are often empty (black), especially on a wide pan or a plane / cylindrical warp. maxInscribedRectCrop chooses how that canvas is cropped at finalize:

  • false (default) — crop to the bounding rectangle of the non-black pixels: keeps every stitched pixel, but can leave black corners where the projection didn't fill.
  • true — crop to the largest axis-aligned rectangle that fits entirely inside the stitched region: clean, straight edges, no black corners, at the cost of more CPU at finalize and a potentially much smaller output on lopsided or ultra-wide pans — which is why it's opt-in.
// Default (false) — bounding-box crop, keeps every stitched pixel:
<Camera onCapture={handleCapture} />

// Opt in — clean inscribed rectangle, no black corners (may shrink the output):
<Camera onCapture={handleCapture} maxInscribedRectCrop={true} />
rectCrop forces this off

When the rectCrop draggable-quad editor is enabled, the native auto-crop (maxInscribedRectCrop) is forced off — the manual crop is the source of truth.

Panorama capture & guidance (v0.16)

These props govern the non-AR panorama capture flow: which device hold is accepted, the live guidance surfaces during the pan, the auto-stop budgets, and what UI (if any) the user sees after the stitch finalizes.

panMode

PanMode ('vertical' | 'horizontal' | 'both'), default 'vertical'.

Which device hold the non-AR pano accepts:

  • 'vertical' — landscape-only, top → bottom. Breaking new default.
  • 'horizontal' — portrait-only, left → right.
  • 'both' — no rotate gate; any hold is accepted.

Starting in the wrong hold is blocked behind the rotate prompt (see rotateToLandscape / rotateToPortrait in guidanceCopy keys).

panGuidance

boolean, default true.

panGuidance is a current prop

Earlier drafts of this page claimed panGuidance was removed. That was wrong — it is a live prop and defaults to true.

Master switch for the in-capture pan-guidance surfaces: the rotate prompt, the pan how-to animation/hint, the "too fast" pill, and the blinking countdown. Set false to suppress all of them. Note that the lateral-drift finalize (lateralBudgetCm) and the post-stitch crop preview (rectCrop / showPreview) have their own independent props and are not governed by panGuidance.

maxPanDurationMs

number, default 0 (disabled).

A hard recording-time ceiling (ms) that acts as a safety cap alongside the primary keyframe-count auto-stop. v0.16 changed the default from 9000 to 0 — the keyframe-count auto-stop is now the default UX, and the time cap is opt-in. When > 0, a blinking countdown is shown and capture auto-finalizes when it reaches 0.

panTooFastThreshold

number, default 0.6 rad/s (resolved fallback).

Gyro rate (rad/s) above which the pan is flagged "too fast" (the amber pill). There is no component-level default; the prop is passed through as usePanMotion's warnMaxRadPerSec, so when omitted it resolves to DEFAULT_WARN_RAD_PER_SEC = 0.6.

Stale JSDoc

The prop JSDoc claims a default of 1.0 rad/s. The real fallback literal is 0.6.

lateralBudgetCm

number, default 4.

Cross-pan (lateral / sideways) drift budget in cm. Once integrated sideways translation exceeds this for the grace window, the capture finalizes with whatever was captured before the drift (carrying the LATERAL_DRIFT_FINALIZE warning). Set 0 to disable the lateral-drift stop.

Stale JSDoc

The prop JSDoc claims a default of 5. The real component default is 4 (usePanMotion's own DEFAULT_LATERAL_BUDGET_CM is also 4).

rectCrop

boolean, default false.

Show the draggable-quad crop editor after a pano finalizes, before onCapture fires. With true, the user drags four corners and confirming perspective-rectifies the stitch to a rectangle.

showPreview

boolean, default false.

Show a plain review screen (Retake / Confirm, no crop box) after a pano finalizes. Ignored when rectCrop is on.

How rectCrop and showPreview interact

Both props control the post-finalize UI, and rectCrop takes precedence:

rectCropshowPreviewWhat the user sees after finalize
falsefalseNothingonCapture fires immediately, no review UI.
falsetruePlain review screen (Retake / Confirm), no crop box.
truefalseDraggable-quad crop editor (drag 4 corners, confirm rectifies).
truetrueCrop editor wins — showPreview is ignored.

When rectCrop is on, the native auto-crop (maxInscribedRectCrop) is forced off so the manual crop is the single source of truth. The crop editor's button labels and the preview's confirm label are all overridable via guidanceCopy (cropConfirm, cropReset, cropUseOriginal, cropRetake, previewConfirm).

guidanceCopy

Partial<GuidanceCopy>, default falls back to DEFAULT_GUIDANCE_COPY.

Copy overrides for every guidance string — the rotate prompt, the pan hint, the "too fast" warning, the lateral-stop popup, the crop/preview buttons, the status banner, and the warning banners. It's a Partial: any key you omit falls back to DEFAULT_GUIDANCE_COPY. See guidanceCopy keys for the full key list and defaults.

<Camera
panMode="vertical"
panGuidance
lateralBudgetCm={4}
rectCrop
guidanceCopy={{
rotateToLandscape: 'Turn your phone sideways',
panHint: 'Sweep slowly, top to bottom',
cropConfirm: 'Looks good',
}}
onCapture={handleCapture}
/>

UI toggles

PropTypeDefaultDescription
enablePhotoModebooleantrueEnable tap-shutter single-photo capture.
enablePanoramaModebooleantrueEnable hold-pan-release panorama capture.
showSettingsButtonbooleanfalseShow the gear button that opens the internal settings modal. Off by default so public consumers don't see it (absorbed into the header's right side when headerTitle is set).
styleStyleProp<ViewStyle>Style applied to the Camera root container.

Photo depth sidecar (iOS)

PropTypeDefaultDescription
captureDepthDatabooleanfalseiOS, non-AR photo path. Save each tap photo's AVDepthData as a <photo>.depth.bin sidecar (float32 metres + JSON header) and return its path as depthPath on the photo result. Stereo depth on dual-camera iPhones, LiDAR-backed absolute depth on Pro models. Silently yields no sidecar on Android, in AR capture, and on single-lens hardware. Adds per-shot latency while depth delivery runs.

See Photo depth sidecar for the sidecar file format, device requirements, and consumption notes.

Flash

Controlled or uncontrolled. See Flash & lenses.

PropTypeDefaultDescription
flash'on' | 'off'— (uncontrolled; internal 'off')Controlled-or-uncontrolled torch state. Omit to let <Camera> own it internally; supply it to take ownership in the parent. Forced to 'off' in AR mode (ARKit/ARCore own the torch).
onFlashChange(next: 'on' | 'off') => voidFires when the user taps the built-in flash button. In uncontrolled mode the internal state has already flipped; in controlled mode the parent must update flash or the toggle is a no-op.
showFlashButtonbooleantrueShow the built-in flash button in the bottom-left slot. Set false to render your own flash chrome and drive the torch via the controlled flash prop.

Header chrome (opt-in)

Setting headerTitle renders a built-in top header; the settings gear is absorbed into it.

PropTypeDefaultDescription
headerTitlestring— (header not rendered)Built-in CaptureHeader title (centred). When undefined the header is not rendered.
onHeaderBack() => void— (no back button)Header back-button callback. When supplied (and headerTitle set) the header renders a back affordance on the left.
headerBackLabelstring'‹ Back'Header back-button label. No effect unless headerTitle and onHeaderBack are both set.
headerGuidancestring— (renders nothing)Optional second-line subtitle below the header title. No effect unless headerTitle is set.
headerColorsCaptureHeaderProps['colors']— (white-on-black)Colour overrides for the built-in header. No effect unless headerTitle is set.

Capture history (thumbnails)

PropTypeDefaultDescription
thumbnailsCaptureThumbnailItem[] ({ id, uri, width?, height? })— (strip skipped)When provided (even as []), Camera renders a built-in CaptureThumbnailStrip above the bottom controls. onCapture results are not auto-added; the host owns the canonical list.
thumbnailsMinnumberMinimum-photos hint for the count line ('n / min'); success colour when reached, warning otherwise.
thumbnailsMaxnumberMaximum-photos hint for the count line ('· max' suffix). No enforcement.
onThumbnailPress(item: CaptureThumbnailItem) => void— (built-in preview)Tap handler for thumbnails. When set, replaces the strip's built-in tap-to-preview modal with the host's own UI.

Post-stitch preview modal

PropTypeDefaultDescription
capturePreview{ imageUri: string; imageWidth?: number; imageHeight?: number; title?: string }— (modal hidden)When set, Camera renders a built-in CapturePreview modal as visible (post-stitch confirmation). undefined hides it.
capturePreviewActionsCapturePreviewAction[]— / [] (only close affordance)Action buttons along the bottom of the CapturePreview modal. Empty/undefined renders no buttons, only the close affordance.
onCapturePreviewClose() => voidFires when the user dismisses the capturePreview modal (close tap, backdrop tap, Android hardware back). Host clears the capturePreview prop in response.

Frame processor (advanced)

PropTypeDefaultDescription
frameProcessorReadonlyFrameProcessor | DrawableFrameProcessor— (lib's internal driver)Optional host-supplied vision-camera frame processor (non-AR mode). Replaces the lib's default processor; compose first-party stitching back via useStitcherWorklet. No effect in AR mode (the vision-camera Camera isn't mounted; host worklets fire via AR dispatch).

Callbacks

PropTypeFires / purpose
onCapture(result: CameraCaptureResult) => voidAlways fires once per capture attempt (v0.16). Carries ok: true (output present, with warnings[]) or ok: false (carrying the CameraError). Gate on ok before reading uri / width / height. See Capture result & errors.
onCaptureSourceChange(source: CaptureSource) => voidThe effective capture source changes (AR ↔ non-AR).
onLensChange(lens: CameraLens) => voidThe selected physical lens changes (1× ↔ 0.5×).
onFramesDropped(info: FramesDroppedInfo) => voidFires once per panorama capture if the progressive-confidence retry loop inside cv::Stitcher dropped one or more input frames. info is { requested: number; included: number }.
onError(err: CameraError) => voidFires on failure — an unchanged mirror of the ok: false onCapture result, so existing error handling keeps working. See Capture result & errors.
onCaptureAbandoned(reason: 'orientation-drift' | 'lateral-drift') => voidFires when the SDK auto-abandons an in-progress capture without producing output. 'orientation-drift' = cross-mode rotation mid-capture; 'lateral-drift' (v0.16) = sideways move before enough frames. No onCapture fires for an abandoned capture.

Output

PropTypeDefaultDescription
outputDirstring (bare path or file:// URI)— (vision-camera tmp dir)Destination directory for captures. When set, photos land at ${outputDir}/photo-${ts}.jpg and panoramas at ${outputDir}/panorama-${ts}.jpg. The host owns dir choice / existence / visibility. Disk failure rejects via onError with OUTPUT_WRITE_FAILED. Requires expo-file-system (optional peer dep).

Settings JSON objects

The flat default* props each seed a single field. v0.16 adds two JSON-object props — stitcher and frameSelection — that let you pass the whole sub-config at once. They are Partial: set only the fields you care about, and any field you set wins over the matching flat default* prop. (The frameSelection.flow object is deep-merged.) Runtime gear-panel edits still override these at capture time.

<Camera
stitcher={{
stitchMode: 'panorama',
warperType: 'spherical',
blenderType: 'feather',
seamFinderType: 'skip',
}}
frameSelection={{
mode: 'flow-based',
maxKeyframes: 8,
overlapThreshold: 0.25,
maxKeyframeIntervalMs: 1500,
flow: { noveltyPercentile: 0.9, maxTranslationCm: 60 },
}}
onCapture={handleCapture}
/>

stitcherBatchStitcherSettings

Plus two CaptureBaseSettings fields (captureSource, debug) that live on the settings tree these fields sit under.

FieldTypeDefaultDescription
captureSource'ar' | 'non-ar''ar'Which camera + tracking source feeds the engine. 'ar' = ARKit/ARCore pose (real translation); 'non-ar' = vision-camera gyro yaw+pitch only, with the JS IMU gate filling translation. (Settings-tree default; the component prop default for defaultCaptureSource is 'non-ar'.)
debugbooleanfalseShow the lib's built-in diagnostic overlay (memory / keyframe / orientation pills, stitch-stats toast, metrics block).
stitchMode'auto' | 'panorama' | 'scans''auto'cv::Stitcher pipeline mode. 'auto' picks panorama/scans at finalize from the translation/rotation ratio; 'panorama' = rotation-only (ORB + BA-Ray + Spherical); 'scans' = affine (Affine + BA-Affine + Plane). Both platforms retry with the opposite mode on degenerate params.
warperType'plane' | 'cylindrical' | 'spherical''plane'Output projection. PANORAMA mode uses it directly; SCANS hard-wires PlaneWarper and ignores it. v0.16 reverted from 'spherical' to 'plane'.
blenderType'multiband' | 'feather''multiband'Pixel blender. 'multiband' = cleaner seams, holds all warped frames in memory; 'feather' = streams, lower peak memory.
seamFinderType'graphcut' | 'skip''graphcut'Seam-finder strategy. 'graphcut' finds optimal seams (pair with multiband); 'skip' streams warp+feed (pair with feather, lowest memory).
enableMaxInscribedRectCropbooleanfalseOutput crop strategy. false crops to the non-black bounding rect; true runs max-inscribed-rect + morph-close (no black corners, more CPU at finalize).

frameSelectionFrameSelectionSettings

FieldTypeDefaultDescription
mode'time-based' | 'pose-based' | 'flow-based''flow-based'Frame-selection strategy. 'time-based' = gate disabled (every frame up to maxKeyframes); 'pose-based' = plane-overlap / angular-delta; 'flow-based' = Shi-Tomasi corners + KLT (more expensive, accurate for translation; default since v0.3).
maxKeyframesnumber6Hard cap on accepted keyframes per capture. Clamped natively to [3, 10]. cv::Stitcher convergence degrades past ~8–10 frames.
overlapThresholdnumber0.20Required NEW-content fraction (0..1) for a candidate to be accepted. (The field docstring's 0.15 is stale; the literal is 0.20.) Lower = more frames / denser overlap. Clamped natively to [0.10, 0.80].
maxKeyframeIntervalMsnumber1500Time-budget force-accept (ms) for both AR + non-AR. When > 0, accepts a keyframe whenever this many ms elapsed since the last, even if novelty is unmet. Counts toward maxKeyframes. 0 disables.
flowFlowGateSettings (optional)DEFAULT_FLOW_GATE_SETTINGSSparse-optical-flow tunables. Consulted only when mode === 'flow-based'. See below.

frameSelection.flowFlowGateSettings

Consulted only when frameSelection.mode === 'flow-based'.

FieldTypeDefaultDescription
noveltyPercentilenumber0.85Percentile aggregating per-feature absolute displacements into a per-axis novelty estimate (V16 change from the pre-V16 median 0.50). Higher picks up leading-edge motion sooner. Clamped to [0.50, 0.99].
evalEveryNFramesnumber5Caller-side throttle — evaluate the flow strategy every Nth frame (~6 Hz at 30 Hz ARCore). Pure CPU savings; doesn't change which frames are accepted. Clamped to [1, 10].
maxTranslationCmnumber50Translation budget (cm). When > 0, force-accepts the next frame after translating more than this since the last keyframe, even below overlapThreshold. 0 disables. Clamped to [0, 100].
maxCornersnumber150Shi-Tomasi corner count. Higher = more robust median, slower detect (~15–25 ms at 150 on a Galaxy A35). Clamped to [50, 300].
qualityLevelnumber0.01Shi-Tomasi quality level. Lower lets weaker corners in (more KLT noise); higher demands stronger corners. Clamped to [0.005, 0.05].
minDistancenumber10Shi-Tomasi minimum distance between detected corners, in working-resolution px (input downscaled to 720-px-longest-side). Higher = more spatially spread features. Clamped to [1, 50].

guidanceCopy keys

Every key is a string; pass a Partial<GuidanceCopy> and unspecified keys fall back to DEFAULT_GUIDANCE_COPY (the defaults shown here).

KeyDefaultDescription
rotateToLandscapeRotate to landscapeCaption pill while waiting for the user to rotate to landscape (panMode: 'vertical').
rotateToPortraitRotate to portraitCaption pill while waiting for the user to rotate to portrait (panMode: 'horizontal').
panHintPan slowly top to bottomShort hint shown with the how-to-pan animation.
tooFastMoving too fast — slow downTransient warning when the pan is too fast.
lateralStopTitleKeep the pan straightPopup title when the user drifts laterally (cross-axis).
lateralStopBodyYou moved sideways. Pan in one direction only — we stitched what you captured.Popup body / guidance for the lateral-drift stop (capture was finalized).
lateralStopDismissGot itPopup dismiss button label.
lateralWrongDirectionTitleFollow the arrowPopup title when lateral drift stopped the capture before enough frames to stitch (nothing produced).
lateralWrongDirectionBodyYou moved the phone the wrong way. Pan slowly in the direction the arrow shows, in one straight line.Popup body for the too-few-frames wrong-direction stop.
cropConfirmCropConfirm button on the crop editor.
cropResetResetReset-corners button on the crop editor.
cropUseOriginalUse original"Emit the stitch un-cropped" button on the crop editor.
cropRetakeRetakeDiscard this capture and return to the camera.
previewConfirmConfirmAccept button in preview-only mode (showPreview without rectCrop): confirms the stitched image as-is.
statusRecordingHold steady — pan slowlyCaptureStatusOverlay banner while a capture is recording (calm green state).
statusStitchingStitching panorama…CaptureStatusOverlay banner while the panorama is being stitched after release.
warnLowFrameUtilizationOnly {included} of {requested} captured frames ({percent}%) could be used — the panorama may be incomplete. Pan more slowly and steadily next time.LOW_FRAME_UTILIZATION warning. Template — keep the {included} / {requested} / {percent} placeholders.
warnLateralDriftFinalizeCapture stopped early because the phone drifted sideways — only the part captured before the drift was stitched.LATERAL_DRIFT_FINALIZE warning.
warnHighPanSpeedThe capture was taken faster than the recommended pace — the result may not be the best. Pan more slowly next time.HIGH_PAN_SPEED warning.
Localizing copy

The same strings can be localized centrally. See i18n for the full workflow.

Enums / allowed values

TypeAllowed valuesNotes
CameraLens'1x', '0.5x'Physical lens selector. 0.5x forces non-AR (AR sessions don't expose the ultra-wide).
CaptureSource'ar', 'non-ar'Effective capture source. 'ar' = ARKit/ARCore; 'non-ar' = vision-camera + IMU.
CaptureSourcesMode'ar', 'non-ar', 'both'Host constraint on allowed capture sources. 'both' shows the AR toggle; a single value hides it (and 'ar' also hides the 0.5× chooser).
StitchMode'auto', 'panorama', 'scans'cv::Stitcher pipeline mode. 'auto' resolves at finalize; 'panorama' = rotation-only; 'scans' = affine/plane.
Blender'multiband', 'feather'Pixel blender. 'multiband' = cleaner seams / more memory; 'feather' = streaming / lower peak memory.
SeamFinder'graphcut', 'skip'Seam-finder strategy. 'graphcut' = optimal seams; 'skip' = stream warp+feed.
Warper'plane', 'cylindrical', 'spherical'Output projection. Used by PANORAMA mode; SCANS hard-wires plane.
PanMode'vertical', 'horizontal', 'both'Device hold the non-AR pano accepts. 'vertical' = landscape-only top→bottom (default); 'horizontal' = portrait-only left→right; 'both' = no rotate gate.
DeviceOrientation'portrait', 'portrait-upside-down', 'landscape-left', 'landscape-right'Device orientation reported by useDeviceOrientation (works under iOS portrait-lock).

See also

  • Capture result & errors — the full CameraCaptureResult union (ok: true photo / panorama and ok: false variants), CaptureWarning codes, and the CameraError taxonomy.
  • Complete example (all options) — a full, copy-pasteable host screen exercising the props on this page: permission, thumbnails, the preview modal, panorama guidance, and onCaptureAbandoned.
  • Recipes — common configurations, copy-paste ready.
  • i18n — localizing guidanceCopy and the recoverable-error copy.