Skip to content

Core API

Most app code should import from @symbiote-native/react, @symbiote-native/vue, @symbiote-native/angular, @symbiote-native/svelte, or @symbiote-native/solid. The core packages explain why those adapters can stay thin and consistent.

Terminal window
pnpm add @symbiote-native/engine

Most apps never install this directly — @symbiote-native/react, @symbiote-native/vue, @symbiote-native/angular, @symbiote-native/svelte, and @symbiote-native/solid all declare it as a peer dependency, so it arrives with the adapter. Install it by hand only when you are writing or debugging an adapter yourself. react-native (>=0.86) is a peer dependency and stays your app’s own top-level dependency either way. @symbiote-native/components installs the same way — see the Components API.

The engine owns the native route:

adapter mutation → JS command buffer → SymbioteTree (C++) → Fabric clone-on-write commit → native views

The retained tree used to live in the JS package itself; it now lives in C++ (core/engine/cpp), and the JS side’s job is building the buffer and crossing into it once per commit instead of once per mutation. The public API below is unchanged either way.

Its public surface includes:

  • the mutation API used by adapters: createElement, appendChild, insertBefore, removeChild, setProp, setEventListener, commit-related operations through surfaces;
  • host instance helpers such as measure, setNativeProps, and dispatchViewCommand;
  • style utilities such as StyleSheet, flattenStyle, and native style processors;
  • platform/runtime utilities such as Platform, PixelRatio, Dimensions, Appearance, Alert, Share, Linking, and Keyboard;
  • native-view metadata hooks such as setNativeViewConfigSource;
  • createAppRegistry, the framework-agnostic core of AppRegistry (registry bookkeeping, sections, the native host-registrar bridge, headless tasks) — see App entry point below.

Users normally reach these through adapter re-exports.

Every adapter exports the same AppRegistry.registerComponent(appKey, () => App) entry point RN apps already use, plus setHostRegistrar to hand it RN’s own AppRegistry so the native Fabric host can find the registered runnable by app key:

import { AppRegistry, setHostRegistrar } from '@symbiote-native/react'; // or '@symbiote-native/vue' / '@symbiote-native/angular' / '@symbiote-native/svelte' / '@symbiote-native/solid'
import { AppRegistry as RNAppRegistry } from 'react-native';
setHostRegistrar(RNAppRegistry);
AppRegistry.registerComponent('MyApp', () => App);

registerSection, runApplication, unmountApplicationComponentAtRootTag, setWrapperComponentProvider, and the headless-task methods (registerHeadlessTask, registerCancellableHeadlessTask, startHeadlessTask, cancelHeadlessTask) round out the same surface RN’s AppRegistry exposes. All of this bookkeeping lives once in @symbiote-native/engine’s createAppRegistry — the only framework-specific piece each adapter supplies is runnableFor, the function that turns a component provider into a mount call (createElement + mount for React, createApp/h for Vue, createComponent for Angular, its own mount for Svelte, and solid-js/universal’s createComponent for Solid).

The component package owns framework-agnostic component logic:

  • pure state machines, for example Switch, Pressable, TextInput, and list windowing logic;
  • pure render helpers that produce Descriptor trees;
  • shared accessibility folding and native component-name resolution;
  • shared event payload helpers and command helpers.

Adapters provide only lifecycle and element bridges:

Layer React Vue Angular Svelte Solid
State lifecycle hooks / refs refs / watches / expose zoneless change detection (signals / fields) $state / $effect runes signals / effects (createSignal / createEffect)
Render bridge descriptorToReact descriptorToVue DescriptorOutlet (proven on some components, migration in progress) a DOM shim over stock compiled output (adapters/svelte/src/dom-shim/), no generic Descriptor walker; each component hand-authors its fixed host tag(s) and reads props/children off known positions, mountDescriptorChildren syncing an already-fixed shape descriptorToSolid: builds a host node once, then re-props it through a spread render effect; a component taking live children (View, Pressable, Modal, …) skips the bridge and emits real JSX instead
Public framework shape callbacks / children emits / slots @Output() EventEmitters / <ng-content> callback props, same names as React, no emits, no @Output() callback props (class for styling); a render-prop child (Pressable, list renderItem) takes a Solid Accessor, not a snapshot value

Native-view wrapper packages can use the same seam:

  • derive native metadata with setNativeViewConfigSource;
  • keep shared view logic in a framework-agnostic package;
  • expose thin React, Vue, Angular, Svelte, and Solid wrappers that map framework props/events to engine props and native events.

Do not import a third-party React Native JavaScript component into Vue. Wrap the native view, not the React component body.