Socket
Book a DemoInstallSign in
Socket

expo-roomplan

Package Overview
Dependencies
Maintainers
1
Versions
12
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

expo-roomplan

React Native implementation of Apple's RoomPlan SDK

1.2.1
latest
Source
npmnpm
Version published
Weekly downloads
38
-83.26%
Maintainers
1
Weekly downloads
 
Created
Source

expo-roomplan

React Native implementation of Apple's RoomPlan SDK.

Read more about it here: Apple - RoomPlan

Usage

useRoomPlan

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:

  • parametric
  • mesh
  • model

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:

  • Not Started
  • Canceled
  • Error
  • OK

RoomPlanView

This tutorial shows how to embed the native RoomPlan scanning UI directly with RoomPlanView and control it imperatively via props.

  • Render a full-screen overlay with basic controls.
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>
  );
}
  • Finish stops capture, shows Apple’s preview, then exports automatically if exportOnFinish is true (default). Add Room stops and restarts to accumulate more rooms.

useRoomPlanView

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>
  );
}

RoomPlanProvider

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>
  );
}

RoomPlanView (Reference)

Props

PropTypeDefaultDescription
scanNamestring"Room"Base filename for exported .usdz and .json.
exportTypeExportTypeParametricExport mode: Parametric, Mesh, or Model.
sendFileLocbooleanfalseWhen true, onExported includes file URLs rather than showing a share sheet.
runningbooleanfalseStarts/stops scanning. Toggle true to begin; false to stop.
exportTriggernumberBump to trigger export. Queued if no room captured yet.
finishTriggernumberBump to stop capture and present Apple’s preview UI.
addAnotherTriggernumberBump to finish current room and immediately start another.
exportOnFinishbooleantrueIf true, finishing also exports after preview.
styleViewStyleStandard React Native style prop.
onStatus({ nativeEvent: { status, errorMessage? }}) => voidReceives status updates: OK, Error, Canceled, etc.
onPreview() => voidCalled when preview UI is presented.
onExported({ nativeEvent: { scanUrl?, jsonUrl? }}) => voidEmitted after export; URLs when sendFileLoc is true.

Notes

  • Finish and Add Room are edge-triggered by changing the trigger numbers (e.g. Date.now()).
  • Export is queued until the first processed room exists; no premature "No rooms captured" errors.

useRoomPlanView (Reference)

Options

OptionTypeDefaultDescription
scanNamestringBase filename for export.
exportTypeExportTypeParametricExport mode.
exportOnFinishbooleantrueAuto-export after finish.
sendFileLocbooleantrueInclude file URLs in onExported.
autoCloseOnTerminalStatusbooleanfalseAutomatically set running=false on OK/Error/Canceled.
onStatusfunctionIntercepts status events.
onPreviewfunctionIntercepts preview event.
onExportedfunctionIntercepts exported event.

Return shape

KeyTypeDescription
viewPropsRoomPlanViewPropsSpread onto RoomPlanView.
controlsobject{ start, cancel, finishScan, addRoom, exportScan, reset }.
stateobject{ isRunning, status, isPreviewVisible, lastExport, lastError }.

RoomPlanProvider (Reference)

Props: identical to useRoomPlanView options. Provides a context with the same return shape as the hook.

Exports

  • RoomPlanProvider: wraps a subtree and initialises the controller and state.
  • useRoomPlanContext(): returns { viewProps, controls, state } from context.
  • RoomPlanViewConsumer: convenience component to render RoomPlanView using viewProps from context.

Installation in managed Expo projects

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.

Installation in bare React Native projects

For bare React Native projects, you must ensure that you have installed and configured the expo package before continuing.

Add the package to your npm dependencies

npm install expo-roomplan

Add this to your expo-module.config.json:

{
  "platforms": ["android", "ios"],
  "android": {
    "moduleName": "ExpoRoomPlan"
  }
}

Configure for Android

Only compatible with iOS 17.0 or higher.

Configure for iOS

Only compatible with iOS 17.0 or higher.

Run npx pod-install after installing the npm package.

Contributing

Contributions are very welcome! Please refer to guidelines described in the contributing guide.

Development Notes

Replace YOUR_TEAM_ID with a real development team in the example project.

Keywords

react-native

FAQs

Package last updated on 30 Aug 2025

Did you know?

Socket

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.

Install

Related posts

SocketSocket SOC 2 Logo

Product

About

Packages

Stay in touch

Get open source security insights delivered straight into your inbox.

  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc

U.S. Patent No. 12,346,443 & 12,314,394. Other pending.