Core Concepts
Understand how the Rocapine Onboarding Studio SDK works under the hood.
Architecture Overview
Onboarding Studio follows a headless-first architecture, separating data management from UI rendering. This gives you maximum flexibility to use pre-built components or build your own UI.
Package Structure
The SDK is split into two packages:
-
@rocapine/react-native-onboarding(Core/Headless)- Data fetching and caching
- State management and hooks
- Type definitions
- No UI components
-
@rocapine/react-native-onboarding-ui(Optional UI)- Pre-built screen components
- Theme system
- Styled components for all page types
Architecture Layers
┌─────────────────────────────────────┐
│ Your React Native App │
│ (Navigation, Routing, Business) │
└──────────────┬──────────────────────┘
│
┌──────────────▼──────────────────────┐
│ UI Layer (Your Choice) │
│ • Pre-built: OnboardingPage │
│ • Custom: Your own components │
│ • Mixed: Both approaches │
└──────────────┬──────────────────────┘
│
┌──────────────▼──────────────────────┐
│ Core SDK (Headless) │
│ • OnboardingProvider (Data) │
│ • useOnboardingStep (Hooks) │
│ • Caching & State Management │
│ • Type-safe data structures │
└──────────────┬──────────────────────┘
│
┌──────────────▼──────────────────────┐
│ CMS Backend │
│ (Supabase - Onboarding Studio) │
└─────────────────────────────────────┘
Headless-First Benefits
- Flexibility: Build completely custom UIs that match your brand
- Bundle Size: Only install UI components you actually use
- Incremental Adoption: Start with pre-built components, migrate to custom UI later
- Framework Agnostic: Core SDK works with any React Native navigation solution
How It Works
1. Initialization
When you wrap your app with OnboardingProvider and ThemeProvider:
<OnboardingProvider
client={client}
customAudienceParams={{ onboardingId: "abc-123" }}
>
<ThemeProvider>
<YourApp />
</ThemeProvider>
</OnboardingProvider>
The SDK:
- Initializes React Query for data fetching (OnboardingProvider)
- Sets up AsyncStorage caching (OnboardingProvider)
- Prepares theme context (ThemeProvider)
- Establishes progress tracking (OnboardingProvider)
2. Data Fetching
When you call useOnboardingQuestions({ stepNumber: 1 }):
const {
step,
isLastStep,
stepsLength,
onboardingMetadata,
steps
} = useOnboardingQuestions({ stepNumber: 1 });
The SDK:
- Checks AsyncStorage cache first (offline support)
- Fetches from CMS if cache is stale or missing
- Uses React Query's
useSuspenseQueryfor automatic loading states - Validates data with Zod schemas
- Updates progress context
3. Rendering
You have two options for rendering:
Option A: Use Pre-built Components (from @rocapine/react-native-onboarding-ui)
import { OnboardingPage } from "@rocapine/react-native-onboarding-ui";
<OnboardingPage step={step} onContinue={handleContinue} />
The pre-built component:
- Inspects the
step.typefield - Routes to the appropriate renderer (
QuestionRenderer,MediaContentRenderer, etc.) - Passes step data and callbacks
- Handles fallback for unimplemented types (sandbox mode)
Option B: Build Your Own UI (Headless approach)
import { useOnboardingStep } from "@rocapine/react-native-onboarding";
const { step } = useOnboardingStep({ stepNumber: 1 });
// Build your own UI based on step.type and step.payload
switch (step.type) {
case "Question":
return <YourCustomQuestionUI step={step} />;
case "MediaContent":
return <YourCustomMediaUI step={step} />;
// ... handle other types
}
This gives you complete control over the UI while still benefiting from the SDK's data fetching, caching, and type safety.
Caching Strategy
AsyncStorage Cache
The SDK uses AsyncStorage to cache onboarding steps for offline access.
Cache Behavior:
- Steps are cached after successful fetch
- Cache is checked before network requests
- Stale data triggers background refresh
- Cache persists across app restarts
Cache Invalidation: You can manually invalidate the cache:
import { useQueryClient } from "@tanstack/react-query";
const queryClient = useQueryClient();
queryClient.invalidateQueries({ queryKey: ["onboardingSteps"] });
Progress Tracking
The SDK automatically tracks progress through your onboarding flow:
Progress Context
const { activeStep, totalSteps, progress } = useContext(OnboardingProgressContext);
activeStep: Current step number (updated byuseOnboardingQuestions)totalSteps: Total number of steps in the flowprogress: Percentage (0-1) of completion
ProgressBar Component
Add <ProgressBar /> once in your app layout, typically inside both providers:
<OnboardingProvider {...props}>
<ThemeProvider>
<ProgressBar /> {/* Global progress indicator */}
<YourApp />
</ThemeProvider>
</OnboardingProvider>
The ProgressBar:
- Reads from progress context automatically
- Shows/hides based on
step.displayProgressHeader - Animates smoothly with Reanimated
- Positions itself at the top of safe area
Step Types
Each step returned by the CMS has this structure:
{
id: string; // Unique identifier
type: string; // "Question" | "MediaContent" | "Carousel" | ...
name: string; // Display name (for debugging)
displayProgressHeader: boolean; // Show/hide progress bar
payload: object; // Type-specific data
customPayload: object | null; // Your custom fields
continueButtonLabel?: string; // Optional CTA text
figmaUrl?: string | null; // Design reference
}
Type Discrimination
The SDK uses the type field for routing:
switch (step.type) {
case "Question":
return <QuestionRenderer step={step} onContinue={onContinue} />;
case "MediaContent":
return <MediaContentRenderer step={step} onContinue={onContinue} />;
// ... more types
}
Runtime Validation
All step data is validated at runtime using Zod schemas:
const QuestionStepSchema = z.object({
id: z.string(),
type: z.literal("Question"),
payload: z.object({
title: z.string(),
subtitle: z.string().nullish(),
answers: z.array(/* ... */),
// ...
}),
// ...
});
If data doesn't match the schema, you'll get a clear error message.
Sandbox Mode
Enable sandbox mode during development:
const client = new OnboardingStudioClient("your-project-id", {
isSandbox: true,
});
Sandbox Features:
- Shows dev messages for unimplemented screen types
- More verbose error messages
- Allows testing without production CMS data
- Auto-continues on unimplemented screens
Production Mode:
- Silently auto-continues on unimplemented screens
- Minimal error messages
- Optimized for end-user experience
Locale Support
Fetch onboarding content in different languages:
<OnboardingProvider locale="fr" />
The locale is passed to the CMS API, which returns localized content based on your Onboarding Studio configuration.
Custom Headers
The CMS API returns custom headers with metadata:
ONBS-Onboarding-Id: The active onboarding IDONBS-Audience-Id: The targeted audienceONBS-Onboarding-Name: Display name of the onboarding
These headers are available in the client but typically not needed in your app logic.
Suspense Boundaries
The SDK uses React Query's useSuspenseQuery, which requires Suspense boundaries:
import { Suspense } from "react";
<Suspense fallback={<LoadingScreen />}>
<OnboardingScreen />
</Suspense>
Expo Router handles Suspense automatically, so you usually don't need to add boundaries manually.
Error Handling
The SDK provides multiple layers of error handling:
- Network Errors: Automatic retry with exponential backoff
- Validation Errors: Clear messages about schema mismatches
- Missing Dependencies: Explicit instructions for required packages
- Sandbox Mode: Development-friendly error messages
Theme System
The SDK provides a theme system via ThemeProvider from the UI SDK with:
- Color Tokens: Semantic color names (primary, neutral, text, etc.)
- Typography Tokens: Font families, sizes, weights, line heights
- Semantic Text Styles: Pre-configured styles (heading1, body, button, etc.)
- Mode Support: Light and dark mode with separate token sets
Learn more in Customization → Theming.
Performance Considerations
Optimizations
- Lazy Loading: Renderers are lazy-loaded per screen type
- Native Driver: Animations use native driver when possible
- Memoization: Components are memoized where appropriate
- Query Deduplication: React Query prevents duplicate requests
Best Practices
- Use pagination for long onboarding flows
- Preload media assets when possible
- Keep payload sizes reasonable
- Cache static assets locally
Next Steps
- 📘 Explore the API Reference for detailed prop documentation
- 🎨 Learn about Customization Options
- 🎭 Review available Page Types