@openeditor/native
v0.0.43
Published
React Native host for the offline OpenEditor embedded runtime.
Readme
@openeditor/native
React Native host for the offline OpenEditor embedded runtime.
The document, selection, history, and DOM NodeViews live inside one internally
scrollable WebView. React Native owns the keyboard toolbar and application
effects such as pickers, sheets, and navigation. The bridge sends commands and
lightweight state/revision events; document JSON only crosses it when the host
explicitly calls getDocument() or flushDocument().
On iOS, the host hides WKWebView's default previous/next/Done accessory bar so
the OpenEditor toolbar is the only bar above the software keyboard. The SDK
sticks that toolbar to the keyboard, reserves its document inset, and enables
interactive keyboard dismissal while the editor scrolls. Consumers can opt
back into the system bar with
webViewProps={{ hideKeyboardAccessoryView: false }}.
The theme prop accepts the same OpenEditor theme-token contract used by the
web React surface.
Host integration
Install the native peer dependencies:
pnpm add expo-glass-effect react-native-keyboard-controller react-native-reanimated react-native-svg react-native-webviewThe package includes an Expo native module that scopes interactive iOS keyboard dismissal to the editor's own WebView. Expo autolinking discovers it; no config plugin or AppDelegate mutation is required. Rebuild the native application after adding the package or changing native dependencies. Mount the SDK provider once at the app root:
import { OpenEditorNativeProvider } from "@openeditor/native";
export default function RootLayout() {
return <OpenEditorNativeProvider>{/* app routes */}</OpenEditorNativeProvider>;
}The self-contained offline runtime is packaged with the component, so Metro asset customization is not required:
const editor = useRef<OpenEditorNativeController>(null);
<OpenEditorNative
ref={editor}
initialDocument={document}
contentInsets={{ top: transparentHeaderHeight, bottom: 16 }}
onDocumentChanged={({ documentRevision }) => {
scheduleAutosave(async () => {
const snapshot = await editor.current?.getDocument({
minimumRevision: documentRevision,
});
if (snapshot) await save(snapshot.document);
});
}}
nativeEffects={{
pickAttachment: pickUploadAndReturnDurableAttachment,
createPage: openCreatePageSheet,
updatePage: updatePageMetadata,
openPage: navigateToPage,
}}
/>The default toolbar is a keyboard-sticky, inset pill that meets the keyboard
edge without a synthetic spacer. It uses native iOS glass and a theme-aware
material fallback elsewhere. A custom renderToolbar returns only the toolbar
content; the SDK still owns its glass surface and positioning, so do not add
another KeyboardStickyView or outer background. Use toolbarContentInset
when the custom toolbar needs more than the default 76-point document clearance,
and toolbarOffset only when the keyboard edge itself needs a different gap.
The editor host must reach the screen's bottom edge. Do not wrap the editor in
a bottom-padding SafeAreaView; the keyboard owns that edge while editing, and
reserving the home-indicator inset there creates a visible toolbar gap.
The block picker derives its available page, image, and attachment commands
from the supplied effect handlers. If the application cannot upload media to a
durable URL, omit the corresponding picker effect rather than returning a
temporary file:// URI.
Do not wrap the component in a React Native ScrollView; give it a bounded
flex layout so the WebView remains the only document scroll owner. Before a
screen transition or background save, call flushDocument() to commit pending
composition and receive the current snapshot.
For a read-only surface, use
<OpenEditorNative initialDocument={document} editable={false} showToolbar={false} />.
