App shell and providers
The app shell creates one OvokClient and makes it available to routes that use the
SDK. Provider ownership is explicit: @ovok/core supplies the client context,
@ovok/native supplies the mobile UI theme and feature components, and your app
supplies navigation and any app-wide services.
Use this page after installation and native setup. For a minimal working sign-in app, follow the quick start; this guide explains where each provider belongs as the app grows.
Provider tree
For an Expo Router app, a typical root looks like this:
KeyboardProvider
└── GestureHandlerRootView
└── OvokProvider @ovok/core
└── ThemeProvider @ovok/native
└── BottomSheetModalProvider optional peer provider
└── Navigation ThemeProvider
└── App routes
Add optional app providers, such as query-cache persistence or localization, where their context is needed. Keep feature providers close to the routes that use them; Bluetooth scans and health authorization should not start just because the app shell mounted.
Initialize the client outside React renders
Initialize the runtime from the entry module before importing this layout, as shown in Polyfills and runtime setup. This example assumes that bootstrap has already run. Keep both the client and storage adapter at module scope so a route render does not recreate the client's identity or session storage.
Put the initializer in a module such as ovok-client.ts; have the root layout and
other app services import that same client instance.
import { OvokClient } from "@ovok/core";
import { ExpoClientStorage } from "@ovok/native";
const baseUrl = process.env.EXPO_PUBLIC_OVOK_BASE_URL;
if (!baseUrl) {
throw new Error("Set EXPO_PUBLIC_OVOK_BASE_URL in your app environment");
}
export const ovokClient = new OvokClient({
baseUrl,
fhirUrlPath: "/fhir",
storage: new ExpoClientStorage(),
});
Use your app's environment setup for baseUrl and optional client configuration.
Expo embeds EXPO_PUBLIC_ values in the app bundle, so they are suitable for public
configuration only. See Installation for native dependencies and
Authentication for session behavior.
Compose the Expo Router root
This example follows the provider boundaries used by the reference app. The SDK theme is separate from the navigation theme, even when both use the same brand palette.
import { BottomSheetModalProvider } from "@gorhom/bottom-sheet";
import { OvokProvider } from "@ovok/core";
import {
DEFAULT_COLORS,
DEFAULT_MULTIPLIERS,
ThemeProvider as OvokThemeProvider,
} from "@ovok/native";
import {
DefaultTheme,
Stack,
ThemeProvider as NavigationThemeProvider,
} from "expo-router";
import type { PropsWithChildren } from "react";
import { GestureHandlerRootView } from "react-native-gesture-handler";
import { KeyboardProvider } from "react-native-keyboard-controller";
import { ovokClient } from "./ovok-client";
function SdkProviders({ children }: PropsWithChildren) {
return (
<OvokProvider client={ovokClient}>
<OvokThemeProvider
theme={{
colors: DEFAULT_COLORS,
dark: false,
spacingMultiplier: DEFAULT_MULTIPLIERS.spacing,
borderRadiusMultiplier: DEFAULT_MULTIPLIERS.borderRadius,
}}
>
<BottomSheetModalProvider>
<NavigationThemeProvider value={DefaultTheme}>
{children}
</NavigationThemeProvider>
</BottomSheetModalProvider>
</OvokThemeProvider>
</OvokProvider>
);
}
export default function RootLayout() {
return (
<KeyboardProvider>
<GestureHandlerRootView style={{ flex: 1 }}>
<SdkProviders>
<Stack />
</SdkProviders>
</GestureHandlerRootView>
</KeyboardProvider>
);
}
OvokProvider is exported by @ovok/core; the SDK ThemeProvider is exported by
@ovok/native. Rename the latter on import when using another provider with the same
name. BottomSheetModalProvider is imported directly from @gorhom/bottom-sheet;
mount it once when a screen uses SDK bottom sheets such as PickerSheet or
DataSync.ManuallyRequestSheet.
If you use SafeAreaProvider, a persisted query client, localization, error
reporting, or app configuration context, add those providers where their hooks and
components need them. The reference app places GestureHandlerRootView around
navigation and uses the keyboard provider above it.
Configure the SDK theme
ThemeProvider applies the React Native Paper theme, loads the bundled DM Sans
fonts, and provides device-list context used by the supported-device UI. Its theme
prop contains:
| Property | Purpose |
|---|---|
colors | Paper colors plus the SDK's semantic and indexed color palette; start with DEFAULT_COLORS and override values your app uses. |
dark | Whether the SDK theme is in dark mode. |
spacingMultiplier | Spacing scale; DEFAULT_MULTIPLIERS.spacing is the baseline. |
borderRadiusMultiplier | Corner-radius scale; DEFAULT_MULTIPLIERS.borderRadius is the baseline. |
Read the active theme with useAppTheme. Navigation has its own color shape and
must receive a navigation theme separately; map the brand colors to navigation's
primary, background, card, text, border, and notification values rather
than passing the SDK color object directly.
The SDK theme provider returns null while its fonts load. Account for that initial
render in the app's launch experience; the provider does not accept a loading
fallback prop.
Place feature providers near their screens
Mount these providers only for flows that use them:
| Provider | Place it | What to keep in mind |
|---|---|---|
BTProvider | Around the Bluetooth screen or flow | Pass a long-lived BleManager and the accepted device declarations. Unmounting stops the provider-owned scan and tears down its managed devices. |
DataSync.AppleHealthAuthorizationProvider | Around the iOS Health import screen | Request only the HealthKit identifiers the feature reads or writes. |
DataSync.AndroidHealthConnectAuthorizationProvider | Around the Android Health Connect screen | Declare platform record permissions in native app configuration as well as in the authorization flow. |
SocketProvider | Around screens that use socket context | Keep the URL, authentication policy, subscriptions, and reconnect UX app-owned. |
Do not mount Bluetooth or health import providers globally unless the product intentionally keeps that workflow active across every route. See the Bluetooth guide, Health data guide, and Background sync for lifecycle and platform details.
Context requirements
Render SDK features below the providers they use:
OvokProvider: authentication forms and account controls, patient and observation features that use the active client or patient, data-sync components, socket context, questionnaire submission helpers, and result handlers that use the client.ThemeProvider: components that calluseAppTheme, the device catalog list, and themed SDK UI.BottomSheetModalProvider: SDK components that open a Gorhom bottom-sheet modal.KeyboardProvider: keyboard-aware forms that usereact-native-keyboard-controller.
When a component renders without expected data, check its provider ancestry and the current authentication or patient state before changing its props. See Architecture and lifecycle for the responsibilities of each boundary.