React Native Quickstart¶
SceneView provides a React Native module that bridges to native SceneView rendering on both Android (Filament) and iOS (RealityKit).
Install¶
iOS: edit the Podfile first¶
Two lines have to be in ios/Podfile before pod install:
platform :ios, '18.0'
pod 'SceneViewSwift',
:podspec => 'https://raw.githubusercontent.com/sceneview/sceneview/main/SceneViewSwift.podspec'
The first because SceneViewSwift's floor is iOS 18.0, so the module's podspec
declares :ios => "18.0", while a stock React Native Podfile uses
min_ios_version_supported (13.4) — pod install then fails with "Specs
satisfying the react-native-sceneview dependency were found, but they required
a higher minimum deployment target".
The second because the module's podspec declares s.dependency
"SceneViewSwift", and that pod is not published on the CocoaPods trunk, so
CocoaPods cannot resolve the name on its own: without the coordinate,
pod install fails with "Unable to find a specification for SceneViewSwift".
Use :path => '<repo-root>' instead if you build from a checkout of this
repository. A tagged URL does not work yet — the root podspec landed after
v4.26.0 was cut, so no existing tag carries it.
Adding SceneViewSwift as a Swift package in Xcode does not replace that
line: this module compiles inside Pods.xcodeproj, which cannot see the host
project's Swift packages.
Xcode 16+ is required to build against that target.
Usage¶
3D Scene¶
import { SceneView } from '@sceneview-sdk/react-native';
export default function ModelViewer() {
return (
<SceneView
style={{ flex: 1 }}
modelNodes={[
{ src: 'models/damaged_helmet.glb', scale: 1.0 }
]}
environment="environments/sky_2k.hdr"
cameraControlMode="orbit"
/>
);
}
AR Scene¶
import { ARSceneView } from '@sceneview-sdk/react-native';
export default function ARViewer() {
return (
<ARSceneView
style={{ flex: 1 }}
modelNodes={[
{ src: 'models/chair.glb', scale: 0.5 }
]}
planeDetection={true}
onPlaneDetected={(event) => {
console.log('Plane detected:', event.nativeEvent);
}}
/>
);
}
How It Works¶
React Native (TypeScript)
└── Native Component
├── Android → SimpleViewManager → ComposeView → SceneView { }
└── iOS → RCTViewManager → SceneViewerHostView → SceneView { }
Props¶
SceneView¶
| Prop | Type | Description |
|---|---|---|
modelNodes |
ModelNode[] |
Array of models to display |
environment |
string |
HDR environment path |
cameraControlMode |
'orbit' \| 'pan' \| 'firstPerson' |
Camera mode. pan/firstPerson are iOS-only |
cameraOrbit |
boolean |
Deprecated, inert on iOS — use cameraControlMode |
onTap |
(event) => void |
Tap event: { x, y, z, nodeName } — the tapped model's world position and its file base name without extension. nodeName is null when no model was hit (and always null for ARSceneView on iOS, see below); the key is always present, so one nodeName == null check covers every view and platform |
ARSceneView (extends SceneView)¶
| Prop | Type | Description |
|---|---|---|
planeDetection |
boolean |
Enable plane detection |
depthOcclusion |
boolean |
Enable LiDAR depth occlusion |
onPlaneDetected |
(event) => void |
Plane detection event |
onTap is inherited from SceneView, but what a hit reports differs by
platform:
- Android hit-tests the AR scene, so a tap on a model reports that model's
file base name exactly as
SceneViewdoes; a tap on a plane or on nothing reportsnull— a plane hit is not a model hit. - iOS always reports
null:SceneViewSwift'sARSceneViewexposes no entity hit-test hook, so its AR tap can only resolve the surface point. Tracked under #2051.
The key is written on every dispatch path of both views on both platforms, so
nodeName == null is still the single correct "the tap hit no model" test. How
often it dispatches differs, though: on iOS a tap that hits no entity fires no
onTap at all (RealityKit's gesture is entity-targeted), where Android
dispatches a 0, 0, 0 miss — so tap-event totals are not comparable across
platforms.
iOS SceneView.onTap is measured, and it works
The 3D onTap above was run on an iPhone 17 Pro Max simulator with a model
rendering: 5 taps on the model, 5 dispatches, the model's base name as
nodeName every time
(#3086).
The sibling Flutter bridge still never fires its 3D onTap on iOS
(#3045). The same run
measured both hosts against the same SceneViewSwift build and the same
entity graph: under Flutter, 6 taps on the model resolved no entity, while
the untargeted gesture arrived every time. That failure belongs to Flutter's
platform-view touch delivery, not to the shared RealityKit path React Native
uses.
Type Definitions¶
interface ModelNode {
src: string; // glTF/GLB path
position?: [number, number, number];
rotation?: [number, number, number];
scale?: number;
animation?: boolean;
}
interface TapEvent {
x: number;
y: number;
z: number;
// The tapped model's file base name without extension, or `null` when the
// tap hit no model — which is every AR tap on iOS (#2051). Never
// `undefined`: every dispatch path writes the key.
nodeName: string | null;
}