API Reference
Complete API documentation for all exported components, hooks, and types.
Components
OnboardingProvider
The root provider component that wraps your app and provides onboarding context for data fetching.
import { OnboardingProvider } from "@rocapine/react-native-onboarding";
<OnboardingProvider
client={client}
locale="en"
customAudienceParams={{ onboardingId: "abc-123" }}
customActions={{
trackCta: async ({ variables }) => {
console.log("trackCta", variables);
},
}}
>
<YourApp />
</OnboardingProvider>
Props
| Prop | Type | Default | Description |
|---|---|---|---|
client | OnboardingStudioClient | Required | Client instance for API communication |
locale | string | "en" | Locale for fetching steps |
customAudienceParams | Record<string, any> | {} | Static audience params (build-time facts such as an onboardingId). Merged under the OnboardingStudio user-property store, which wins per key, and read once when the onboarding is served — a later change applies to the next serve (next mount or launch). |
customActions | CustomActions | {} | Map of named handlers invokable from ComposableScreen Button.actions { type: "custom", function, variables? }. See Custom Actions |
CustomActionHandler
import type {
CustomActionHandler,
CustomActions,
ComposableVariableEntry,
} from "@rocapine/react-native-onboarding";
type CustomActionHandler = (args: {
variables: Record<string, ComposableVariableEntry | undefined>;
}) => void | Promise<void>;
type CustomActions = Record<string, CustomActionHandler>;
variables is filtered to the names listed in the action's variables array (missing keys yield undefined). Handlers may return a Promise — the action chain awaits before proceeding.
ThemeProvider
The theme provider component that wraps your app and provides theme context. Import from the UI SDK.
import { ThemeProvider } from "@rocapine/react-native-onboarding-ui";
<ThemeProvider
initialColorScheme="light"
customTheme={{
colors: { primary: "#FF5733" },
typography: {
fontFamily: { title: "Poppins-Bold" }
}
}}
>
<YourApp />
</ThemeProvider>
Props
| Prop | Type | Default | Description |
|---|---|---|---|
initialColorScheme | "light" | "dark" | "light" | Initial color scheme |
customTheme | DeepPartial<Theme> | - | Custom theme to override both light and dark mode tokens |
customLightTheme | DeepPartial<Theme> | - | Custom theme tokens for light mode only |
customDarkTheme | DeepPartial<Theme> | - | Custom theme tokens for dark mode only |
OnboardingPage
Central routing component that renders the appropriate page based on step type.
<OnboardingPage
step={step}
onContinue={handleContinue}
theme={theme}
customComponents={customComponents}
/>
Props
| Prop | Type | Description |
|---|---|---|
step | OnboardingStepType | Step data to render |
onContinue | (args?: any) => void | Callback when user continues |
theme | Theme | Optional theme override (defaults to theme from ThemeProvider) |
customComponents | CustomComponents | Custom components for the onboarding |
isSandbox | boolean | Enable sandbox mode for development |
ProgressBar
Global progress indicator component. Add once in your app layout, typically inside both providers.
<OnboardingProvider {...props}>
<ThemeProvider>
<ProgressBar isProgressBarVisible={isProgressBarVisible} progressPercentage={progressPercentage} />
<YourApp />
</ThemeProvider>
</OnboardingProvider>
Props
None. Reads from progress context automatically.
Page Renderers
Individual renderer components for each page type.
import {
QuestionRenderer,
MediaContentRenderer,
CarouselRenderer,
PickerRenderer,
LoaderRenderer,
RatingsRenderer,
CommitmentRenderer,
} from "@rocapine/react-native-onboarding";
Common Props:
| Prop | Type | Description |
|---|---|---|
step | StepType | Type-specific step data |
onContinue | (args?: any) => void | Callback when user continues |
Example:
<MediaContentRenderer
step={mediaContentStep}
onContinue={(args) => console.log(args)}
/>
Hooks
useOnboardingQuestions
Hook for accessing onboarding steps in your pages.
import { useOnboardingQuestions } from "@rocapine/react-native-onboarding";
const {
step,
isLastStep,
stepsLength,
onboardingMetadata,
steps
} = useOnboardingQuestions({
stepNumber: 1,
});
Parameters
| Param | Type | Description |
|---|---|---|
stepNumber | number | The current step number (1-indexed) |
Returns
| Property | Type | Description |
|---|---|---|
step | OnboardingStepType | Current step data |
isLastStep | boolean | True if this is the last step |
stepsLength | number | Total number of steps |
onboardingMetadata | OnboardingStepMetadata | Metadata from API (id, name, audienceId, etc.) |
steps | OnboardingStepType[] | Array of all steps |
useTheme
Hook for accessing theme tokens and utilities. Import from the UI SDK.
import { useTheme } from "@rocapine/react-native-onboarding-ui";
const { theme, colorScheme, toggleTheme } = useTheme();
Returns
| Property | Type | Description |
|---|---|---|
theme | Theme | Current theme tokens (colors + typography) |
colorScheme | "light" | "dark" | Current color scheme |
toggleTheme | () => void | Function to toggle between light and dark mode |
useCustomComponents
Hook for accessing custom component overrides.
import { useCustomComponents } from "@rocapine/react-native-onboarding";
const customComponents = useCustomComponents();
const Button = customComponents.QuestionAnswerButton || DefaultButton;
Returns
| Property | Type | Description |
|---|---|---|
customComponents | CustomComponents | Custom component overrides |
Classes
OnboardingStudioClient
Client for communicating with the Rocapine CMS backend.
import { OnboardingStudioClient } from "@rocapine/react-native-onboarding";
const client = new OnboardingStudioClient(projectId, options);
Constructor
new OnboardingStudioClient(
projectId: string,
options?: {
appVersion?: string;
assignmentId?: string | false | (() => string | false | null | undefined | Promise<string | false | null | undefined>);
isSandbox?: boolean;
baseUrl?: string;
fallbackOnboarding?: Onboarding;
timeout?: number;
cacheKey?: string;
}
)
Parameters
| Param | Type | Default | Description |
|---|---|---|---|
projectId | string | Required | Your Rocapine project ID |
options.appVersion | string | - | Your app version (sent to the CMS for version targeting) |
options.assignmentId | string | false | () => string | false | Promise<…> | Installation id | Stable per-person id sent with every request so a person keeps the same variant across launches. A getter is called on every request. false opts out. See Sticky variant assignment. |
options.isSandbox | boolean | false | Enable sandbox/draft mode (always fetches fresh, never caches) |
options.baseUrl | string | Rocapine prod | Override the CMS endpoint (testing/self-host) |
options.fallbackOnboarding | Onboarding | - | Payload returned on fetch failure (never cached) |
options.timeout | number | - | End-to-end timeout in ms per request, covering assignmentId resolution and the fetch together |
options.cacheKey | string | - | Custom cache key. Persists the payload under rocapine-onboarding-sdk-{cacheKey} cache-first with no background revalidation (a pinned version survives across launches until you clearCache()). Omit for the default key with stale-while-revalidate. See Caching Strategy. |
Sticky variant assignment
Every getSteps and getPaywalls request carries an assignmentId in the
X-Assignment-Id request header, so the server can put the same person in the
same variant every time. It is never put on the URL, because the id may be one
of your user ids and request URLs end up in server logs. A custom audience
param named assignmentId is dropped, never sent.
The SDK resolves it per request, in this order:
options.assignmentId: a string, or a getter (sync or async) called on every request.- An installation id: a UUID the SDK generates on first use and persists in
AsyncStorage under
rocapine-assignment-id. It survives app updates,clearCache()andOnboardingStudio.reset(). It is lost when the app is uninstalled or its storage is cleared. - Nothing: if storage cannot be read or written, the
X-Assignment-Idheader is not sent, rather than sent with an id the next launch would not repeat.
A host value of undefined, null or "" falls back to the installation id
for that request.
A getter that throws, rejects, or takes longer than 2 seconds fails closed:
that request sends no assignmentId, and the installation id is not read,
created or written. A getter that cannot answer may be one that would have
answered false (no consent yet), so the SDK never substitutes the stored id
for it. Resolving the id counts against options.timeout, so a slow getter
cannot delay fallbackOnboarding past it.
To assign by your RevenueCat App User ID (stable across devices once the user is identified):
import Purchases from "react-native-purchases";
// App entry, at module scope: configure RevenueCat FIRST, then the SDK.
Purchases.configure({ apiKey: REVENUECAT_API_KEY });
OnboardingStudio.init({
projectId,
assignmentId: () => Purchases.getAppUserID(),
});
Purchases.configure() must have run before the first getSteps or
getPaywalls request, and OnboardingProvider / PaywallProvider fetch when
they mount. If your RevenueCat setup is asynchronous (for example, you load the
API key first), await it before mounting them:
const [purchasesReady, setPurchasesReady] = useState(false);
useEffect(() => {
setupPurchases().then(() => setPurchasesReady(true)); // resolves after Purchases.configure()
}, []);
if (!purchasesReady) return <Splash />;
return <OnboardingProvider>{/* … */}</OnboardingProvider>;
If a request runs before configure, the getter throws, so that request sends
no assignmentId (it fails closed, see above) and the server picks a variant at
random. On a later launch the same person is assigned by their RevenueCat id and
can land in a different variant.
The SDK never reads RevenueCat on its own: the getter is the only way to use the RevenueCat App User ID. Detecting it automatically would make the id depend on whether RevenueCat was configured before the first request, so the same install could send different ids on different launches.
Privacy and opting out
What is stored, and when: on the first getSteps or getPaywalls request
without a host id, the SDK writes one random UUID to AsyncStorage under
rocapine-assignment-id, and sends it in the X-Assignment-Id header on that
request and every later one. It is not derived from any device or advertising identifier, and
it is not shared with any other SDK. It stays until the app is uninstalled or its
storage is cleared. If you pass your own id, the SDK sends that instead and
stores nothing. Declare it accordingly in your privacy disclosures, for example
as a device or user identifier used for app functionality.
To send nothing, pass false, or return false from the getter. The
X-Assignment-Id header is then not sent, and the installation id is never read, created or written. Every
request is then assigned independently, with no stickiness across requests.
null and "" are not an opt-out: they fall back to the installation id.
To wait for consent, return false until it is given:
OnboardingStudio.init({
projectId,
assignmentId: () => (hasConsented() ? undefined : false), // undefined → installation id
});
An installation id written before an opt-out stays on disk. It is not sent while
the opt-out applies. To delete it, call clearAssignmentId():
// Consent withdrawn: stop sending an id, then delete the stored one.
setConsented(false); // your getter now returns false
await OnboardingStudio.clearAssignmentId(); // or client.clearAssignmentId()
clearAssignmentId() removes rocapine-assignment-id from AsyncStorage and
forgets the id in memory. It works before init. A request that is already
resolving the id when you call it sends none, even if your getter is still
pending, and a first-launch id still being written is not brought back. It
rejects if the removal fails, or if it is still waiting after 2 seconds. Each
queued storage operation is bounded this way, but only for the caller. The
operations still run strictly one after another, so a removal never overtakes a
write already in flight. While a write or removal hangs, the requests queued
behind it wait 2 seconds each and then send no assignmentId. That wait counts
against options.timeout, which covers the whole request. A read that hangs
does not block later requests. A timed-out request never mints an id, unless its
write was already in flight. That write lands before any later removal. Make the getter
return false first: otherwise the next request without a host id mints a
new installation id, which can put the person in a different variant. Use
that deliberately if you want a fresh assignment. OnboardingStudio.reset() does
not call it, so logging out does not move anyone to another variant.
The server picks the variant from the id. If the id a person sends changes, for example from an anonymous id to a logged-in user id, that person can move to another variant once, and is counted under both ids. To avoid it, pass an id that is already final on the first request, or keep the default installation id.
Methods
getSteps(params)
Fetches onboarding steps from the CMS.
const { steps, headers } = await client.getSteps({
locale: "en",
onboardingId: "abc-123",
});
clearAssignmentId()
Deletes the stored installation id sent as assignmentId. Same effect as
OnboardingStudio.clearAssignmentId(), because the id is shared by the whole
process. See Privacy and opting out.
await client.clearAssignmentId();
clearCache()
Removes this client's cached onboarding payload from AsyncStorage (the key derived from options.cacheKey, or the default key). The next query mount or app launch is then a cache miss and refetches. To force an in-session refetch as well, also invalidate the React Query key ["onboardingQuestions", ...].
await client.clearCache();
Utilities
getTextStyle
Utility function to get semantic text styles from theme.
import { getTextStyle } from "@rocapine/react-native-onboarding";
const textStyle = getTextStyle(theme, "heading1");
Parameters
| Param | Type | Description |
|---|---|---|
theme | Theme | Theme object from useTheme() |
styleName | TextStyleName | Name of the semantic style |
TextStyleName Options
heading1- Large headings (32px, semibold)heading2- Medium headings (24px, semibold)heading3- Small headings (18px, medium)body- Body text (16px, regular)bodyMedium- Medium body text (16px, medium)label- Labels (14px, medium)caption- Captions (12px, regular)button- Button text (16px, medium)
Returns
{
fontSize: number;
fontWeight: string;
lineHeight: number;
fontFamily?: string;
}
Types
OnboardingStepType
Union type of all step types:
type OnboardingStepType =
| QuestionStepType
| MediaContentStepType
| CarouselStepType
| PickerStepType
| LoaderStepType
| RatingsStepType
| CommitmentStepType;
OnboardingStepMetadata
Metadata returned from the API containing information about the onboarding flow:
interface OnboardingStepMetadata {
id: string;
name?: string;
audienceId?: string;
audienceName?: string;
audienceOrder?: number;
locale?: string;
draft?: boolean;
variantKey?: string | null;
deploymentId?: number | null;
}
| Property | Type | Description |
|---|---|---|
id | string | Unique onboarding ID |
name | string? | Display name of the onboarding |
audienceId | string? | Targeted audience ID |
audienceName | string? | Targeted audience name |
audienceOrder | number? | Audience priority order |
locale | string? | Locale of the onboarding |
draft | boolean? | Whether this is a draft version |
variantKey | string | null? | Variant of the onboarding the user was served; the parent onboarding reports the reserved MAIN_VARIANT_KEY ("main"). null and absent mean the same thing (no variant information, e.g. an older server); treat them identically |
deploymentId | number | null? | Published deployment that was served; null for a draft payload |
MAIN_VARIANT_KEY is exported from @rocapine/react-native-onboarding:
import { MAIN_VARIANT_KEY } from "@rocapine/react-native-onboarding";
const variant = metadata.variantKey ?? undefined; // null and absent: no variant info
const isMain = variant === MAIN_VARIANT_KEY; // "main"
ThemeTokens
Partial theme override type:
interface ThemeTokens {
colors?: {
primary?: string;
secondary?: string;
disable?: string;
tertiary?: {
tertiary1?: string;
tertiary2?: string;
tertiary3?: string;
};
neutral?: {
highest?: string;
higher?: string;
high?: string;
medium?: string;
low?: string;
lower?: string;
lowest?: string;
};
surface?: {
lowest?: string;
lower?: string;
low?: string;
medium?: string;
high?: string;
higher?: string;
highest?: string;
opposite?: string;
};
text?: {
primary?: string;
secondary?: string;
tertiary?: string;
opposite?: string;
disable?: string;
};
};
typography?: {
fontFamily?: {
title?: string;
text?: string;
tagline?: string;
};
fontSize?: {
xs?: number;
sm?: number;
md?: number;
lg?: number;
xl?: number;
"2xl"?: number;
"3xl"?: number;
"4xl"?: number;
};
fontWeight?: {
regular?: string;
medium?: string;
semibold?: string;
bold?: string;
extrabold?: string;
};
lineHeight?: {
tight?: number;
normal?: number;
relaxed?: number;
};
textStyles?: {
heading1?: TextStyle;
heading2?: TextStyle;
heading3?: TextStyle;
body?: TextStyle;
bodyMedium?: TextStyle;
label?: TextStyle;
caption?: TextStyle;
button?: TextStyle;
};
};
}
CustomComponents
Custom component overrides interface:
interface CustomComponents {
QuestionAnswerButton?: React.ComponentType<QuestionAnswerButtonProps>;
QuestionAnswersList?: React.ComponentType<QuestionAnswersListProps>;
}
Component Props Interfaces
QuestionAnswerButtonProps
interface QuestionAnswerButtonProps {
answer: { label: string; value: string };
selected: boolean;
onPress: () => void;
theme: Theme;
index: number;
isFirst: boolean;
isLast: boolean;
}
QuestionAnswersListProps
interface QuestionAnswersListProps {
answers: Array<{ label: string; value: string }>;
selected: Record<string, boolean>;
onAnswerPress: (value: string) => void;
multipleAnswer: boolean;
theme: Theme;
}
Default Exports
Export default components for composition:
import {
DefaultQuestionAnswerButton,
DefaultQuestionAnswersList,
} from "@rocapine/react-native-onboarding";
These allow you to wrap or extend the default implementations while maintaining the base styling.
Type Guards
isQuestionStep
import { isQuestionStep } from "@rocapine/react-native-onboarding";
if (isQuestionStep(step)) {
// TypeScript knows this is QuestionStepType
console.log(step.payload.answers);
}
Similar type guards exist for all step types.
Next Steps
- 📡 View the OpenAPI Specification for the REST API endpoint
- 🎨 Learn about Theming
- 🔧 Explore Custom Components
- 🎭 Review Page Types