React API
Primitives (view, text, pressable, text-input, scroll-view, …) are
plain lowercase intrinsic tags — write them directly in JSX, no import needed.
Import only what’s still a real component or module from @symbiote-native/react:
import { StyleSheet } from '@symbiote-native/react';The React adapter intentionally feels close to React Native. The difference is
below the component surface: host mutations go through @symbiote-native/engine.
Installation
Section titled “Installation”pnpm add @symbiote-native/react react-native reactreact-native and react stay your app’s own top-level dependencies — the
adapter replaces only the JS renderer that drives them, and react-native is
the Metro version anchor, so it has to be pinned at the app root.
@symbiote-native/engine is a peer dependency and installs alongside.
Component shape
Section titled “Component shape”| Concern | React API |
|---|---|
| Event callbacks | onPress, onValueChange (shared by switch/text-input), onLayout |
| Children | children?: ReactNode |
| Render children | list components only (FlatList, SectionList, …) — a primitive tag like pressable has no component body to call a child function against; wire onPressIn/onPressOut into local state instead |
| Refs | React refs to host instances or component handles |
| Styles | style={styles.root} with React Native-style objects, or className for a registered CSS class (see the Styling guide) |
Common events
Section titled “Common events”<pressable onPress={event => {}} onLongPress={event => {}} /><switch value={enabled} onValueChange={(value, event) => setEnabled(value)} /><text-input value={text} onValueChange={(text, event) => setText(text)} /><view onLayout={event => {}} />Refs and handles
Section titled “Refs and handles”Host primitives expose the shared host instance where available. Composed components expose component-specific handles:
import { useRef } from 'react';import type { ITextInputHandle } from '@symbiote-native/react';
const inputRef = useRef<ITextInputHandle>(null);
<text-input ref={inputRef} value="" onValueChange={() => {}} />;inputRef.current?.focus();Runtime modules
Section titled “Runtime modules”The adapter re-exports stable runtime utilities and modules so app code can keep one import root:
import { Alert, Dimensions, Platform, StyleSheet,} from '@symbiote-native/react';Some modules are pure engine utilities (Platform, StyleSheet, PixelRatio,
PlatformColor, DynamicColorIOS). Others are native-bridge consumers
(Alert, Share, Linking, Keyboard, Vibration, ActionSheetIOS,
BackHandler, ToastAndroid, PermissionsAndroid, AccessibilityInfo,
I18nManager, Settings, LayoutAnimation, InteractionManager,
StatusBar) that are shared or thinly adapted depending on lifecycle needs.
useWindowDimensions/useColorScheme hooks and findNodeHandle are also
re-exported.
On Android, Keyboard and Settings do nothing until you also install
@symbiote-native/android:
keyboardDidShow/keyboardDidHide never fire, because Android’s only stock
keyboard-event source is ReactRootView’s layout listener and the bridgeless
surface SymbioteNative mounts never triggers it, and Settings reads back null,
because RN’s Settings has no stock Android implementation at all. That package
supplies both native modules (KeyboardObserver, SettingsManager), is picked
up by Gradle autolinking, and exports nothing from JS — installing it is the
whole integration.
Animations and gestures
Section titled “Animations and gestures”Animated (both the JS and native driver) and PanResponder are re-exported
from @symbiote-native/react — see the Animations guide
for the full surface and per-driver tradeoffs.
Portals
Section titled “Portals”import { createPortal } from '@symbiote-native/react';
createPortal(children, containerNodeOrRef, key?);createPortal renders children into a different node than the calling
component’s own place in the tree — the same primitive React DOM’s
createPortal provides, backed by react-reconciler’s Fiber-level portal
instead of a DOM node. Stock React Native cannot support this: its Fabric host
config runs in persistent mode and never implements the mutation-mode
container operations a portal needs. @symbiote-native/react is mutation-mode, so
this works structurally where it never could in real RN.
The target (containerNodeOrRef) must be an already-mounted SymbioteNative host
node or SymbioteSurface — typically a ref to a persistent “overlay host”
view near the app root — not a CSS-selector-style string. v1 scope: the
target must live in the same surface as the portal’s call site; portaling
into a second, independently-mounted surface isn’t wired yet.
Vue has the equivalent through its own Teleport, Angular through
PortalDirective/PortalOutletDirective, and Svelte and Solid through their
own Portal — see the Vue,
Angular, Svelte,
and Solid API references.
Cross-surface content (createTunnel)
Section titled “Cross-surface content (createTunnel)”import { createTunnel } from '@symbiote-native/react';
const overlayTunnel = createTunnel(); // module-level singleton, importable from both surfaces
function OverlayHost() { return ( <view style={styles.overlayHost}> <overlayTunnel.Out /> </view> );}
function Toast() { return toastVisible ? ( <overlayTunnel.In> <ToastCard /> </overlayTunnel.In> ) : null;}createPortal only reaches a target in the same surface. createTunnel is
for two independently mount()-ed surfaces that share no Fabric tree at all —
a split-screen embed, a system-level overlay surface. In/Out share
nothing but a small store: In registers its children wherever it renders,
any surface; Out reads that store and paints in whichever surface actually
mounts it. Neither touches a Fabric node directly, so there’s no ref-timing
gotcha and no isSymbioteNode guard to satisfy.
Vue, Angular, Svelte, and Solid all have the same primitive — see their own API references.
App entry point (AppRegistry)
Section titled “App entry point (AppRegistry)”@symbiote-native/react exports the same AppRegistry/setHostRegistrar entry point
described in the Core API. A
component passed to setWrapperComponentProvider receives the app root as an
ordinary React children prop:
import { AppRegistry } from '@symbiote-native/react';import type { ReactNode } from 'react';
function ThemeWrapper({ children }: { children?: ReactNode }) { return <ThemeProvider>{children}</ThemeProvider>;}
AppRegistry.setWrapperComponentProvider(() => ThemeWrapper);AppRegistry.registerComponent('MyApp', () => App);Boundary
Section titled “Boundary”Do not pass React component packages to non-React adapters. A third-party React Native package that ships a JavaScript React component still uses the React dispatcher internally. Non-React adapters need native-view wrappers instead.