System UI
@symbiote-native/system-ui wraps
expo-system-ui — setting and
reading the root view’s background color — so every SymbioteNative adapter can reach it, not
just React. Like device and local auth,
both exports here are one-shot async calls with no per-instance state, so there is no
hook/composable/service to wrap — the React, Vue, Angular, and Svelte entry points are plain
re-exports of the same core.
| OS platform | Support |
|---|---|
| iOS | ✅ live |
| Android | ✅ live |
| Framework adapter | Support |
|---|---|
| React | ✅ live |
| Vue | ✅ live |
| Angular | ✅ live |
| Svelte | ✅ live |
Installation
Section titled “Installation”npm install @symbiote-native/system-uiexpo-system-ui and expo-modules-core come along as regular dependencies, pinned to exact
versions — never install either yourself, and never add the expo meta-package to your project
(it bundles its own Metro/Babel pipeline, which conflicts with this project’s own).
expo-system-ui needs no runtime permission on either platform — it only sets/reads a stored
background color, nothing gated by a permission prompt.
All four adapters — React, Vue, Angular, Svelte — re-export the exact same two functions; there is no per-adapter hook/composable/service to reach for, since nothing here holds live state or a subscription.
import { useEffect } from 'react';import { Text, View } from '@symbiote-native/react';import { setBackgroundColorAsync } from '@symbiote-native/system-ui/react';
export default function RootScreen() { useEffect(() => { void setBackgroundColorAsync('black'); }, []);
return ( <View> <Text>Root background set to black</Text> </View> );}<script setup lang="ts">import { onMounted } from 'vue';import { Text, View } from '@symbiote-native/vue';import { setBackgroundColorAsync } from '@symbiote-native/system-ui/vue';
onMounted(() => { void setBackgroundColorAsync('black');});</script>
<template> <View> <Text>Root background set to black</Text> </View></template>import { Component } from '@angular/core';import { Text, View } from '@symbiote-native/angular';import { setBackgroundColorAsync } from '@symbiote-native/system-ui/angular';
@Component({ standalone: true, imports: [Text, View], template: ` <View> <Text>Root background set to black</Text> </View> `,})export class RootScreen { constructor() { void setBackgroundColorAsync('black'); }}There’s no per-instance service to inject() here — both functions are plain exports off the
core package, called straight in the constructor or wherever the app’s root is set up.
<script lang="ts"> import { Text, View } from '@symbiote-native/svelte'; import { setBackgroundColorAsync } from '@symbiote-native/system-ui/svelte';
$effect(() => { void setBackgroundColorAsync('black'); });</script>
<View><Text>Root background set to black</Text></View>Functions
Section titled “Functions”| Signature | Description |
|---|---|
setBackgroundColorAsync(color: ColorValue | null): Promise<void> |
Changes the root view background color. Call this outside of your component tree (e.g. in the root file), since it affects the whole app. null clears the override rather than setting an actual color |
getBackgroundColorAsync(): Promise<ColorValue | null> |
Gets the current root view background color, in hex format. null if the background color is not set |
setBackgroundColorAsync(null)clears the override rather than setting an actual color — it’s passed straight through to the native module without runningprocessColor.- On web, the raw
ColorValueis passed through untouched instead of being run through RN’sprocessColor— matching upstream, sinceprocessColoris a native-color-parsing step that doesn’t apply on that platform. - On iOS/Android, a non-null color is run through RN’s own
processColorbefore reaching the native module — the same conversion RN’s style pipeline uses internally.
How the wrapper works
Section titled “How the wrapper works”@symbiote-native/system-ui ships zero React/Vue/Angular logic in expo-system-ui itself — its
two functions are hand-ported, verbatim, into this package’s own core/, resolving the native
module through expo-modules-core’s requireNativeModule rather than the expo meta-package
this project never installs:
packages/system-ui/src/├── core/ # framework-agnostic: setBackgroundColorAsync/getBackgroundColorAsync;│ # native-module.ts resolves the native module via expo-modules-core's│ # requireNativeModule├── react/ # @symbiote-native/system-ui/react — export * from '../core'├── vue/ # @symbiote-native/system-ui/vue — export * from '../core'├── angular/ # @symbiote-native/system-ui/angular — export * from '../core'└── svelte/ # @symbiote-native/system-ui/svelte — export * from '../core'Same shape as device’s and local auth’s
adapter entries: single-file re-exports with no lifecycle code at all, since every export
here is a stateless one-shot async call — there is nothing for a hook, composable, or service to
subscribe to or clean up. The native code itself is never vendored or copied —
expo-modules-autolinking resolves it straight out of node_modules (see the native setup
guide).