
Product
Introducing Pull Request Stories to Help Security Teams Track Supply Chain Risks
Socket’s new Pull Request Stories give security teams clear visibility into dependency risks and outcomes across scanned pull requests.
expo-roomplan
Advanced tools
React Native implementation of Apple's RoomPlan SDK.
Read more about it here: Apple - RoomPlan
RoomPlanProvider with its RoomPlanViewConsumer for cross‑tree, context‑based control.
useRoomPlanView - hook for local, component‑scoped control.
RoomPlanView - embeddable component driven by props.
useRoomPlan - get up and running quickly with a pre-built view controller.
The simplest way to use this package is with the useRoomPlan
hook:
const { startRoomPlan } = useRoomPlan();
async function startScan() {
try {
await startRoomPlan("My Scan");
} catch (error) {
console.error("Error: ", error);
}
}
startRoomPlan
is an async function that starts a new instance of a UIViewController with the RoomCaptureViewDelegate and RoomCaptureSessionDelegate protocols from RoomPlan. It provides instructions throughout the scan. Once it's finished, you can either add another room to the structure or export what you have. You are then redirected back to the previous screen you were on.
It accepts one parameter, a string for the name of the exported USDZ and JSON from RoomPlan. You can also add other options by providing a config object to the hook:
const { startRoomPlan } = useRoomPlan({ exportType: ExportType.Mesh, sendFileLoc: true });
By default, it exports to parametric generates an ActivityViewController to share via AirDrop or email or other methods.
There are three export types:
You can read more about them here: Apple - RoomPlan USD Export Options
sendFileLoc
is an option that lets you opt out of the ActivityViewController flow and receive the URLs of the exported files manually.
There's also a const called roomScanStatus
that the hook returns:
const { roomScanStatus } = useRoomPlan();
It watches the internal status of the scan. It has 4 different states:
This tutorial shows how to embed the native RoomPlan scanning UI directly with RoomPlanView and control it imperatively via props.
import React, { useState } from "react";
import { SafeAreaView, View, Text, Pressable, StyleSheet } from "react-native";
import { RoomPlanView, ExportType } from "expo-roomplan";
export default function SimpleRoomPlanScreen() {
const [show, setShow] = useState(false);
const [running, setRunning] = useState(false);
const [finishTrigger, setFinishTrigger] = useState<number | undefined>();
const [addAnotherTrigger, setAddAnotherTrigger] = useState<
number | undefined
>();
return (
<SafeAreaView style={{ flex: 1 }}>
<Pressable
onPress={() => {
setShow(true);
setRunning(true);
}}
>
<Text>Start Scan</Text>
</Pressable>
{show && (
<View style={StyleSheet.absoluteFill}>
<RoomPlanView
style={StyleSheet.absoluteFill}
scanName="MyRoom"
exportType={ExportType.Parametric}
sendFileLoc
running={running}
finishTrigger={finishTrigger}
addAnotherTrigger={addAnotherTrigger}
onStatus={(e) => console.log("status", e.nativeEvent)}
onPreview={() => console.log("preview")}
onExported={(e) => console.log("exported", e.nativeEvent)}
/>
<SafeAreaView
style={{
position: "absolute",
top: 16,
left: 16,
right: 16,
flexDirection: "row",
justifyContent: "space-between",
}}
>
<Pressable
onPress={() => {
setRunning(false);
setShow(false);
}}
>
<Text>Cancel</Text>
</Pressable>
<Pressable onPress={() => setFinishTrigger(Date.now())}>
<Text>Finish</Text>
</Pressable>
<Pressable onPress={() => setAddAnotherTrigger(Date.now())}>
<Text>Add Room</Text>
</Pressable>
</SafeAreaView>
</View>
)}
</SafeAreaView>
);
}
Prefer this hook to avoid manually juggling triggers. It returns the props to spread onto RoomPlanView, controller methods, and state.
import React, { useEffect, useState } from "react";
import { SafeAreaView, View, Text, Pressable, StyleSheet } from "react-native";
import { RoomPlanView, useRoomPlanView, ExportType } from "expo-roomplan";
export default function HookDemo() {
const [overlay, setOverlay] = useState(false);
const { viewProps, controls, state } = useRoomPlanView({
scanName: "HookRoom",
exportType: ExportType.Parametric,
exportOnFinish: true,
sendFileLoc: true,
autoCloseOnTerminalStatus: true,
onStatus: (e) => console.log("status", e.nativeEvent),
onPreview: () => console.log("preview"),
onExported: (e) => console.log("exported", e.nativeEvent),
});
useEffect(() => {
if (overlay && !state.isRunning) setOverlay(false);
}, [overlay, state.isRunning]);
return (
<SafeAreaView style={{ flex: 1 }}>
<Pressable
onPress={() => {
setOverlay(true);
controls.start();
}}
>
<Text>Open Scanner</Text>
</Pressable>
{overlay && (
<View style={StyleSheet.absoluteFill}>
<RoomPlanView style={StyleSheet.absoluteFill} {...viewProps} />
<SafeAreaView
style={{
position: "absolute",
top: 16,
left: 16,
right: 16,
flexDirection: "row",
justifyContent: "space-between",
}}
>
<Pressable
onPress={() => {
controls.cancel();
setOverlay(false);
}}
>
<Text>Cancel</Text>
</Pressable>
<Pressable onPress={controls.finishScan}>
<Text>Finish</Text>
</Pressable>
<Pressable onPress={controls.addRoom}>
<Text>Add Room</Text>
</Pressable>
</SafeAreaView>
</View>
)}
</SafeAreaView>
);
}
Use the provider to control RoomPlanView anywhere in a subtree via context. Render RoomPlanViewConsumer or call useRoomPlanContext for controls/state.
import React, { useState, useEffect } from "react";
import { SafeAreaView, View, Text, Pressable, StyleSheet } from "react-native";
import {
RoomPlanProvider,
RoomPlanViewConsumer,
useRoomPlanContext,
ExportType,
} from "expo-roomplan";
function TopBar({ onClose }: { onClose: () => void }) {
const { controls } = useRoomPlanContext();
return (
<SafeAreaView
style={{
position: "absolute",
top: 16,
left: 16,
right: 16,
flexDirection: "row",
justifyContent: "space-between",
}}
>
<Pressable
onPress={() => {
controls.cancel();
onClose();
}}
>
<Text>Cancel</Text>
</Pressable>
<Pressable onPress={controls.finishScan}>
<Text>Finish</Text>
</Pressable>
<Pressable onPress={controls.addRoom}>
<Text>Add Room</Text>
</Pressable>
</SafeAreaView>
);
}
export default function ContextDemo() {
const [overlay, setOverlay] = useState(false);
return (
<RoomPlanProvider
scanName="ContextRoom"
exportType={ExportType.Parametric}
exportOnFinish
sendFileLoc
autoCloseOnTerminalStatus
>
<Inner overlay={overlay} setOverlay={setOverlay} />
</RoomPlanProvider>
);
}
function Inner({
overlay,
setOverlay,
}: {
overlay: boolean;
setOverlay: (v: boolean) => void;
}) {
const { controls, state } = useRoomPlanContext();
useEffect(() => {
if (overlay && !state.isRunning) setOverlay(false);
}, [overlay, state.isRunning]);
return (
<SafeAreaView style={{ flex: 1 }}>
<Pressable
onPress={() => {
setOverlay(true);
controls.start();
}}
>
<Text>Open with Context</Text>
</Pressable>
{overlay && (
<View style={StyleSheet.absoluteFill}>
<RoomPlanViewConsumer style={StyleSheet.absoluteFill as any} />
<TopBar onClose={() => setOverlay(false)} />
</View>
)}
</SafeAreaView>
);
}
Props
Prop | Type | Default | Description |
---|---|---|---|
scanName | string | "Room" | Base filename for exported .usdz and .json. |
exportType | ExportType | Parametric | Export mode: Parametric, Mesh, or Model. |
sendFileLoc | boolean | false | When true, onExported includes file URLs rather than showing a share sheet. |
running | boolean | false | Starts/stops scanning. Toggle true to begin; false to stop. |
exportTrigger | number | — | Bump to trigger export. Queued if no room captured yet. |
finishTrigger | number | — | Bump to stop capture and present Apple’s preview UI. |
addAnotherTrigger | number | — | Bump to finish current room and immediately start another. |
exportOnFinish | boolean | true | If true, finishing also exports after preview. |
style | ViewStyle | — | Standard React Native style prop. |
onStatus | ({ nativeEvent: { status, errorMessage? }}) => void | — | Receives status updates: OK, Error, Canceled, etc. |
onPreview | () => void | — | Called when preview UI is presented. |
onExported | ({ nativeEvent: { scanUrl?, jsonUrl? }}) => void | — | Emitted after export; URLs when sendFileLoc is true. |
Notes
Options
Option | Type | Default | Description |
---|---|---|---|
scanName | string | — | Base filename for export. |
exportType | ExportType | Parametric | Export mode. |
exportOnFinish | boolean | true | Auto-export after finish. |
sendFileLoc | boolean | true | Include file URLs in onExported. |
autoCloseOnTerminalStatus | boolean | false | Automatically set running=false on OK/Error/Canceled. |
onStatus | function | — | Intercepts status events. |
onPreview | function | — | Intercepts preview event. |
onExported | function | — | Intercepts exported event. |
Return shape
Key | Type | Description |
---|---|---|
viewProps | RoomPlanViewProps | Spread onto RoomPlanView. |
controls | object | { start, cancel, finishScan, addRoom, exportScan, reset }. |
state | object | { isRunning, status, isPreviewVisible, lastExport, lastError }. |
Props: identical to useRoomPlanView options. Provides a context with the same return shape as the hook.
Exports
For a step-by-step guide on using this library in a managed Expo app (including privacy manifests and the config plugin), see the tutorial here.
For bare React Native projects, you must ensure that you have installed and configured the expo
package before continuing.
npm install expo-roomplan
Add this to your expo-module.config.json
:
{
"platforms": ["android", "ios"],
"android": {
"moduleName": "ExpoRoomPlan"
}
}
Only compatible with iOS 17.0 or higher.
Only compatible with iOS 17.0 or higher.
Run npx pod-install
after installing the npm package.
Contributions are very welcome! Please refer to guidelines described in the contributing guide.
Replace YOUR_TEAM_ID
with a real development team in the example project.
FAQs
React Native implementation of Apple's RoomPlan SDK
The npm package expo-roomplan receives a total of 26 weekly downloads. As such, expo-roomplan popularity was classified as not popular.
We found that expo-roomplan demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Product
Socket’s new Pull Request Stories give security teams clear visibility into dependency risks and outcomes across scanned pull requests.
Research
/Security News
npm author Qix’s account was compromised, with malicious versions of popular packages like chalk-template, color-convert, and strip-ansi published.
Research
Four npm packages disguised as cryptographic tools steal developer credentials and send them to attacker-controlled Telegram infrastructure.