React Native SDK

Embed Retor in a React Native / Expo app — composable bottom sheets built on @gorhom/bottom-sheet.

Installation

Install peer dependencies:

# Expo
npx expo install react-native-webview react-native-gesture-handler react-native-reanimated react-native-svg expo-blur
npm install @gorhom/bottom-sheet lucide-react-native @retor/react-native

# bare React Native
npm install react-native-webview react-native-gesture-handler react-native-reanimated react-native-svg @gorhom/bottom-sheet lucide-react-native expo-blur @retor/react-native
cd ios && pod install

expo-blur is optional — used for the default blurred sheet background. Pass a custom backgroundComponent to any sheet if you don't want it.

Wrap your app root in GestureHandlerRootView (per @gorhom/bottom-sheet requirements):

import { GestureHandlerRootView } from "react-native-gesture-handler";

export default function App() {
  return (
    <GestureHandlerRootView style={{ flex: 1 }}>
      <YourApp />
    </GestureHandlerRootView>
  );
}

Quick Start — Default UI

import { View } from "react-native";
import { Viewer, Hud, ProjectSheet, LineDetailSheet, AddNoteSheet } from "@retor/react-native";

export default function Scene() {
  return (
    <View style={{ flex: 1 }}>
      <Viewer projectId="abc123">
        <Hud>
          <ProjectSheet />
          <LineDetailSheet />
          <AddNoteSheet />
        </Hud>
      </Viewer>
    </View>
  );
}

Customising the visuals

Each sheet accepts snapPoints, renderHeader, and a children slot. Use <LinesCarousel> and <LineTagList> with render-prop children to swap in your own native components.

import { Pressable, Text } from "react-native";
import { ProjectSheet, LinesCarousel, LineDetailSheet, LineTagList, useViewer, type RetorLine } from "@retor/react-native";

function MyLineCard({ line }: { line: RetorLine }) {
  const { openLine } = useViewer();
  return (
    <Pressable
      onPress={() => openLine(line._id)}
      style={{ width: 200, padding: 16, backgroundColor: "#222", borderRadius: 16 }}
    >
      <Text style={{ color: "white", fontWeight: "600" }}>{line.name}</Text>
    </Pressable>
  );
}

<ProjectSheet snapPoints={["20%", "50%"]}>
  <LinesCarousel>
    {(line) => <MyLineCard line={line} />}
  </LinesCarousel>
</ProjectSheet>

<LineDetailSheet>
  <LineTagList>
    {(tag, isActive) => (
      <Pressable style={{ padding: 12 }}>
        <Text style={{ color: isActive ? "white" : "gray" }}>{tag.name}</Text>
      </Pressable>
    )}
  </LineTagList>
</LineDetailSheet>

Notes

Open <AddNoteSheet> by calling useAddNote().open(tagId?) — typically from a "+" button on the active tag in your LineTagList. The form collects text + private/public, then fires onNoteSubmit on the parent <Viewer>. Persistence is your responsibility — re-pass updated notes back via <Notes>.

import { useState } from "react";
import { View, Pressable, Text } from "react-native";
import {
  Viewer, Hud, ProjectSheet, LineDetailSheet, LineTagList, AddNoteSheet, Notes,
  useAddNote, type RetorTag,
} from "@retor/react-native";

function AddButton() {
  const { open } = useAddNote();
  return <Pressable onPress={() => open()}><Text>+</Text></Pressable>;
}

export default function Scene() {
  const [notes, setNotes] = useState<RetorTag[]>([]);

  return (
    <View style={{ flex: 1 }}>
      <Viewer
        projectId="abc123"
        onNoteSubmit={({ text, tagId, lineId, position }) => {
          if (!position) return;
          setNotes((prev) => [
            ...prev,
            { _id: `note-${Date.now()}`, name: text, position, objectId: lineId ?? undefined },
          ]);
        }}
      >
        <Notes notes={notes} />
        <Hud>
          <ProjectSheet />
          <LineDetailSheet>
            <LineTagList>
              {(tag, isActive) => (
                <View style={{ flexDirection: "row", padding: 12 }}>
                  <Text style={{ flex: 1, color: isActive ? "white" : "gray" }}>{tag.name}</Text>
                  {isActive && <AddButton />}
                </View>
              )}
            </LineTagList>
          </LineDetailSheet>
          <AddNoteSheet />
        </Hud>
      </Viewer>
    </View>
  );
}

Hooks

Identical to the React SDK — same API, same return types.

CoverPhoto

import { CoverPhoto } from "@retor/react-native";

<CoverPhoto projectId="abc123" style={{ width: 200, height: 120 }} />