Skip to main content

Page Types

The SDK includes pre-built renderers for common onboarding patterns.

Overview

Each page type is designed for a specific onboarding use case. All types share common properties and are customizable through theming, custom components, or custom renderers.


Available Page Types

Question

Interactive questions with single or multiple choice answers.

Use cases:

  • User preferences
  • Quiz-style onboarding
  • Survey questions
  • Profile building

Key features:

  • Single or multiple selection
  • Customizable answer buttons
  • Theme-aware styling
  • Support for custom components

Payload:

{
title: string;
subtitle?: string;
answers: Array<{
label: string;
value: string;
}>;
multipleAnswer: boolean;
}

Example:

{
id: "preferences",
type: "Question",
payload: {
title: "What are your goals?",
subtitle: "Select all that apply",
answers: [
{ label: "Build muscle", value: "muscle" },
{ label: "Lose weight", value: "weight" },
{ label: "Improve flexibility", value: "flexibility" },
],
multipleAnswer: true
}
}

Dependencies: None


MediaContent

Display images or videos with title and description.

Use cases:

  • Welcome screens
  • Feature explanations
  • Visual onboarding
  • Tutorial steps

Key features:

  • Video, Image, Lottie, or Rive support
  • Optional title and description
  • Centered media display
  • Responsive sizing

Payload:

{
title?: string;
description?: string;
media: {
type: "video" | "image" | "lottie" | "rive";
url?: string;
localPathId?: string;
};
}

Example:

{
id: "welcome",
type: "MediaContent",
payload: {
title: "Welcome to FitApp",
description: "Your personal fitness companion",
media: {
type: "image",
url: "https://example.com/welcome.png"
}
}
}

Dependencies: None


Multi-slide horizontal pagination with page indicators.

Use cases:

  • Feature tours
  • Multi-step tutorials
  • Swipeable content
  • Progressive disclosure

Key features:

  • Horizontal scrolling
  • Page indicators (dots)
  • Per-slide media and text
  • Automatic "Next" / "Continue" button labels

Payload:

{
screens: Array<{
title?: string;
description?: string;
media?: MediaSource;
}>;
}

Example:

{
id: "tour",
type: "Carousel",
payload: {
screens: [
{
title: "Track Your Progress",
description: "Monitor your fitness journey",
media: { type: "image", url: "https://..." }
},
{
title: "Set Goals",
description: "Define and achieve your targets",
media: { type: "image", url: "https://..." }
}
]
}
}

Dependencies: None


Picker

Type-specific input pickers for structured data.

Use cases:

  • Collect specific user data
  • Profile information
  • Preferences with constrained options

Key features:

  • Native picker UI
  • Type-specific implementations
  • Unit conversions (weight, height)
  • Range validation

Supported types:

  • weight - Weight with kg/lb units
  • height - Height with cm/ft+in units
  • age - Age picker
  • gender - Gender selection
  • name - Name input
  • date - Date picker

Payload:

{
title: string;
subtitle?: string;
pickerType: "weight" | "height" | "age" | "gender" | "name" | "date";
defaultValue?: string;
}

Example:

{
id: "user-weight",
type: "Picker",
payload: {
title: "What's your weight?",
pickerType: "weight",
defaultValue: "70-kg"
}
}

Dependencies: @react-native-picker/picker

npx expo install @react-native-picker/picker

Loader

Sequential progress animation with optional carousel.

Use cases:

  • Loading states
  • Processing animations
  • Wait screens with progress
  • Educational "Did you know?" moments

Key features:

  • Sequential step animations
  • Progress bars (0% → 100%)
  • Checkmarks on completion
  • Optional carousel with images
  • Configurable duration

Payload:

{
title: string;
steps: Array<{
label: string; // Text while loading
completed: string; // Text when done
}>;
didYouKnowImages?: Array<MediaSource>;
duration?: number; // Milliseconds per step (default: 2000)
}

Example:

{
id: "analyzing",
type: "Loader",
payload: {
title: "Analyzing your profile",
steps: [
{
label: "Processing data",
completed: "Data processed"
},
{
label: "Building recommendations",
completed: "Recommendations ready"
}
],
duration: 2000
}
}

Dependencies: None


Ratings

App store rating prompts with social proof.

Use cases:

  • Request app reviews
  • Show user testimonials
  • Build trust with social proof
  • Increase app ratings

Key features:

  • Star rating UI
  • Opens native App Store review
  • Social proof cards (optional)
  • Customizable testimonials

Payload:

{
title: string;
subtitle?: string;
socialProofs?: Array<{
name: string;
rating: number;
comment: string;
avatar?: string;
}>;
}

Example:

{
id: "rate-us",
type: "Ratings",
payload: {
title: "Enjoying FitApp?",
subtitle: "Your feedback helps us improve!",
socialProofs: [
{
name: "Sarah M.",
rating: 5,
comment: "This app changed my life!",
}
]
}
}

Dependencies: expo-store-review

npx expo install expo-store-review

Commitment

User commitment and agreement screens.

Use cases:

  • Terms acceptance
  • Commitment statements
  • Agreements
  • Signature collection

Key features:

  • Checkbox for agreement
  • Optional signature (requires Skia)
  • Info boxes for important notes
  • Required acknowledgment before continuing

Payload:

{
title: string;
subtitle?: string;
commitmentText: string;
requireSignature?: boolean;
infoBoxes?: Array<{
title: string;
description: string;
icon?: string;
}>;
}

Example:

{
id: "terms",
type: "Commitment",
payload: {
title: "Terms of Service",
commitmentText: "I agree to the Terms of Service and Privacy Policy",
requireSignature: false,
infoBoxes: [
{
title: "Privacy First",
description: "We never share your data without permission"
}
]
}
}

Dependencies (signature only): @shopify/react-native-skia

npx expo install @shopify/react-native-skia

ComposableScreen

Build arbitrary onboarding screens entirely from CMS data — no custom renderer required. You define a tree of layout and media elements that is rendered directly to native components.

Use cases:

  • Fully custom layouts without shipping app updates
  • Cards, feature highlights, or rich text screens
  • Animation-heavy screens (Lottie or Rive)
  • Any screen that doesn't fit a pre-built type

Key features:

  • Recursive element tree (YStack, XStack, ZStack, SafeAreaView, Text, Image, Lottie, Rive, Icon, Video, Input, RadioGroup, CheckboxGroup, Button, DatePicker, Carousel)
  • Full flexbox layout support
  • Border, radius, overflow, and opacity control
  • Dimension constraints (width, height, min/max)
  • Spacing with both padding and margin
  • Text color defaults to theme.colors.text.primary
  • Lottie, Rive, Video, and DatePicker as optional peer deps — graceful fallback if not installed
  • Variable contextInput, RadioGroup, CheckboxGroup, and DatePicker elements write into shared state; Text elements interpolate those values with {{variableName}} expressions

Element types:

ElementRenders asDirectionPeer dep
YStack<View>column
XStack<View>row
ZStack<View>depth (z-axis)
SafeAreaView<SafeAreaView>columnreact-native-safe-area-context
Text<Text>
Image<Image>
IconLucide icon— (bundled)
Input<TextInput>
RadioGroupRadio item list
CheckboxGroupCheckbox item list
Button<TouchableOpacity>
CarouselHorizontal slidesreact-native-reanimated-carousel
DatePickerNative date/time picker@react-native-community/datetimepicker
LottieLottieViewlottie-react-native
Rive<Rive>rive-react-native
Video<VideoView>expo-video

Stack props (YStack / XStack):

PropTypeNotes
gapnumberSpace between children
padding / paddingHorizontal / paddingVerticalnumber
margin / marginHorizontal / marginVerticalnumber
flex / flexShrink / flexWrapnumber | stringflexShrink defaults to 1 inside XStack
alignItems"flex-start" | "center" | "flex-end" | "stretch"
alignSelf"auto" | "flex-start" | "flex-end" | "center" | "stretch" | "baseline"
justifyContent"flex-start" | "center" | "flex-end" | "space-between" | "space-around"
width / height / minWidth / maxWidth / minHeight / maxHeightnumber
backgroundColor / borderColorstring
borderWidth / borderRadiusnumber
overflow"hidden" | "visible" | "scroll"Use "hidden" to clip rounded corners
opacitynumber

ZStack props:

ZStack accepts only the BaseBoxProps subset listed below. Flex/flow props such as gap, alignItems, justifyContent, and flexWrap are not honored — children are absolutely positioned, so flex layout does not apply.

PropTypeNotes
width / height / minWidth / maxWidth / minHeight / maxHeightnumberExplicit height is recommended — absolute children do not size the parent
padding / paddingHorizontal / paddingVerticalnumberApplied to the ZStack container
margin / marginHorizontal / marginVerticalnumber
flex / flexShrink / flexGrownumberApply to the ZStack itself within a parent flex container
alignSelf"auto" | "flex-start" | "flex-end" | "center" | "stretch" | "baseline"
backgroundColor / backgroundGradientstring / GradientBackground
borderWidth / borderRadius / borderColornumber | string
overflow"hidden" | "visible" | "scroll"Use "hidden" to clip overflowing children to rounded corners
opacitynumber
ZStack behavior

Children are layered in declaration order: first child = bottom, last child = top. Each child is wrapped in position: "absolute" filling the container, so flex props on ZStack itself (gap, alignItems, justifyContent) have no effect — instead control the layout of each layer's content via the layer's own props (e.g. wrap content in a YStack with justifyContent: "flex-end" to anchor it to the bottom).

The wrapper for each child uses pointerEvents="box-none", so touches pass through transparent regions to layers underneath.

SafeAreaView props:

SafeAreaView mirrors the props of SafeAreaView from react-native-safe-area-context. Use it to inset content from system UI (notch, home indicator) inside a ComposableScreen. Other renderers wrap themselves in SafeAreaView automatically; ComposableScreen does not, so opt in here when needed.

PropTypeNotes
mode"padding" | "margin"Defaults to "padding". "margin" is useful when the SafeAreaView itself has a background that should not extend into the inset region
edgesEdge[] or { top?, right?, bottom?, left? }Edge is "top" | "right" | "bottom" | "left". Object form maps each edge to "off" | "additive" | "maximum" for fine-grained control
All BaseBoxPropsflex, padding, backgroundColor, borderRadius, etc.

SafeAreaView example:

{
"id": "safe-root",
"type": "SafeAreaView",
"props": { "flex": 1, "edges": ["top", "bottom"] },
"children": [
{ "id": "content", "type": "YStack", "props": { "padding": 24, "gap": 16 }, "children": [ /* ... */ ] }
]
}
OnboardingTemplate no longer applies insets

As of 1.15.0, OnboardingTemplate does not apply safe-area insets. Built-in page types (Question, MediaContent, Carousel, etc.) now wrap themselves with SafeAreaView edges={["top","bottom"]}. For ComposableScreen, place a SafeAreaView UIElement at the root of your tree (or omit it for full edge-to-edge rendering).

Image props:

PropTypeNotes
urlstringRequired — remote image URI
widthnumberDefaults to "100%"
heightnumberWhen omitted, aspectRatio is used to size the image
aspectRationumberUsed when height is absent; defaults to 16/9
resizeMode"cover" | "contain" | "stretch" | "center"Defaults to "cover"
borderRadius / borderWidthnumber
borderColorstring
opacitynumber
margin / marginHorizontal / marginVerticalnumber
padding / paddingHorizontal / paddingVerticalnumber

Text props:

PropTypeNotes
contentstringRequired
mode"plain" | "expression""plain" (default) renders content as-is; "expression" interpolates {{variableName}} patterns from the variable context
fontSize / fontWeight / letterSpacing / lineHeightnumber | string
fontFamilystringFont family name; must be loaded by the host app (e.g. via expo-font)
colorstringDefaults to theme.colors.text.primary
textAlign"left" | "center" | "right"
backgroundColor / borderColorstring
padding / paddingHorizontal / paddingVerticalnumber
margin / marginHorizontal / marginVerticalnumber
borderWidth / borderRadius / opacitynumber

Input props:

PropTypeNotes
variableNamestringContext key — value is written to shared composableVariables on every keystroke
placeholderstringPlaceholder text
placeholderColorstringDefaults to theme.colors.text.tertiary
defaultValuestringInitial value; also written to context on mount if variableName is set
keyboardType"default" | "email-address" | "numeric" | "phone-pad" | "decimal-pad" | "url" | "number-pad" | "ascii-capable" | "numbers-and-punctuation" | "name-phone-pad" | "twitter" | "web-search" | "visible-password"Defaults to "default"
returnKeyType"done" | "go" | "next" | "search" | "send" | "default" | "emergency-call" | "google" | "join" | "route" | "yahoo" | "none" | "previous"Defaults to "done"
autoCapitalize"none" | "sentences" | "words" | "characters"Defaults to "sentences"
secureTextEntrybooleanMask input for passwords; defaults to false
maxLengthnumberMaximum character count
multilinebooleanEnable multi-line input; defaults to false
numberOfLinesnumberVisible line count when multiline is true
editablebooleanDefaults to true
colorstringText color; defaults to theme.colors.text.primary
fontSizenumberDefaults to 16
textAlign"left" | "center" | "right"
padding / paddingHorizontal / paddingVerticalnumberInner padding; defaults to 12
backgroundColorstringDefaults to theme.colors.neutral.lowest
borderWidthnumberDefaults to 1
borderRadiusnumberDefaults to 8
borderColorstringDefaults to theme.colors.neutral.low
alignSelf"auto" | "flex-start" | "flex-end" | "center" | "stretch" | "baseline"
width / heightnumber
opacitynumber
margin / marginHorizontal / marginVerticalnumber

RadioGroup props:

PropTypeNotes
itemsArray<{ label: string; value: string }>Required — list of options
variableNamestringContext key — selected entry (value + label) is written to shared composableVariables
defaultValuestringPre-selects the matching item on mount; also written to context
direction"vertical" | "horizontal"Layout direction; defaults to "vertical"
gapnumberSpace between items; defaults to 8
itemBackgroundColorstringUnselected item background; defaults to "transparent"
itemSelectedBackgroundColorstringSelected item background; defaults to primary + 15% opacity
itemBorderColorstringUnselected item border; defaults to theme.colors.neutral.low
itemSelectedBorderColorstringSelected item border; defaults to theme.colors.primary
itemBorderRadiusnumberDefaults to 8
itemBorderWidthnumberDefaults to 1
itemColorstringUnselected label color; defaults to theme.colors.text.primary
itemSelectedColorstringSelected label color; defaults to theme.colors.primary
itemFontSizenumber
itemFontWeightstring
itemFontFamilystring
itemPaddingnumberDefaults to 12 when no itemPaddingHorizontal/Vertical is set
itemPaddingHorizontal / itemPaddingVerticalnumber
alignSelf"auto" | "flex-start" | "flex-end" | "center" | "stretch" | "baseline"
width / heightnumberContainer dimensions
margin / marginHorizontal / marginVerticalnumber
padding / paddingHorizontal / paddingVerticalnumber
borderWidth / borderRadius / borderColornumber | stringContainer border
opacitynumber

CheckboxGroup props:

PropTypeNotes
itemsArray<{ label: string; value: string }>Required — list of options; values must be unique
variableNamestringContext key — selected values stored as JSON.stringify(string[]), label as comma-joined display names
defaultValuesstring[]Pre-selects matching items on mount; must reference valid item values
direction"vertical" | "horizontal"Layout direction; defaults to "vertical"
gapnumberSpace between items; defaults to 8
itemBackgroundColorstringUnselected item background; defaults to "transparent"
itemSelectedBackgroundColorstringSelected item background; defaults to primary + 10% opacity
itemBorderColorstringUnselected item border; defaults to theme.colors.neutral.low
itemSelectedBorderColorstringSelected item border; defaults to theme.colors.primary
itemBorderRadiusnumberDefaults to 8
itemBorderWidthnumberDefaults to 1
itemColorstringUnselected label color; defaults to theme.colors.text.primary
itemSelectedColorstringSelected label color; defaults to theme.colors.primary
itemFontSizenumber
itemFontWeightstring
itemFontFamilystring
itemPaddingnumberDefaults to 12 when no itemPaddingHorizontal/Vertical is set
itemPaddingHorizontal / itemPaddingVerticalnumber
alignSelf"auto" | "flex-start" | "flex-end" | "center" | "stretch" | "baseline"
width / heightnumberContainer dimensions
margin / marginHorizontal / marginVerticalnumber
padding / paddingHorizontal / paddingVerticalnumber
borderWidth / borderRadius / borderColornumber | stringContainer border
opacitynumber

Button props:

PropTypeNotes
labelstringRequired — button text
variant"filled" | "outlined" | "ghost"Visual style; filled = solid primary background, outlined = border only, ghost = no background or border
actionsButtonAction[]Ordered list run on press. See Button actions below. When omitted (and no legacy action), the press is a no-op
action"continue"Deprecated — back-compat alias. When actions is absent and action === "continue", runtime treats it as actions: ["continue"]
backgroundColorstringOverrides variant background color
colorstringLabel text color
fontSizenumber
fontWeightstring
fontFamilystring
textAlign"left" | "center" | "right"
alignSelf"auto" | "flex-start" | "center" | "flex-end" | "stretch"
width / heightnumber
margin / marginHorizontal / marginVerticalnumber
padding / paddingHorizontal / paddingVerticalnumber
borderWidth / borderRadius / borderColornumber | string
opacitynumber

Button actions

Button.actions is an ordered array. Each entry is one of:

  • "continue" — advances to the next onboarding step (calls the onContinue callback wired to the renderer).
  • { type: "custom", function: string, variables?: string[] } — invokes a developer-injected handler registered on OnboardingProvider.customActions. variables lists the keys to read from the live ComposableScreen variable map and forward to the handler.
  • { type: "setVariable", name: string, value: string, label?: string } — writes to the variable map ({ value, label }). Pair with "continue" to set a discriminator a downstream nextStep rule can branch on; the value is visible to the same-tick branch evaluation.

Execution rules:

  • Sequential. Each action awaits the previous before starting.
  • Async-aware. If a custom handler returns a Promise, the runtime awaits it.
  • "continue" is terminal — any actions after it are ignored.
  • A thrown error in a custom handler is logged via console.error and the remaining chain is aborted.
  • An unknown function name (no matching key in customActions) emits console.warn and the chain skips to the next action.
{
"id": "primary-cta",
"type": "Button",
"props": {
"label": "Get Started",
"variant": "filled",
"actions": [
{ "type": "custom", "function": "trackCta", "variables": ["name", "plan"] },
"continue"
]
}
}

Register the matching handlers on OnboardingProvider:

import { OnboardingProvider } from "@rocapine/react-native-onboarding";

<OnboardingProvider
client={client}
customActions={{
trackCta: async ({ variables }) => {
// variables → { name?: { value, label? }, plan?: { value, label? } }
await analytics.track("cta_pressed", {
name: variables.name?.value,
plan: variables.plan?.value,
});
},
}}
>
{children}
</OnboardingProvider>

See Custom Actions for the full handler signature and patterns.

DatePicker props:

PropTypeNotes
variableNamestringContext key — selected date stored as ISO 8601 string (value) and locale-formatted string (label, e.g. "Apr 23, 2026")
defaultValuestringISO date string; used as initial value if no persisted value exists
minimumDatestringISO date string — earliest selectable date
maximumDatestringISO date string — latest selectable date
mode"date" | "time" | "datetime"Defaults to "date"
display"default" | "spinner" | "calendar" | "clock" | "compact" | "inline"Platform-specific display style; iOS defaults to "spinner", Android defaults to "default"
textColorstringPicker text color; defaults to theme.colors.text.primary
accentColorstringPicker accent/highlight color; defaults to theme.colors.primary
localestringBCP 47 locale tag (e.g. "fr-FR")
alignSelf"auto" | "flex-start" | "flex-end" | "center" | "stretch" | "baseline"
width / heightnumberContainer dimensions
margin / marginHorizontal / marginVerticalnumber
padding / paddingHorizontal / paddingVerticalnumber
borderWidth / borderRadius / borderColornumber | stringContainer border
opacitynumber
Android

On Android, DatePicker renders as a Pressable showing the current value. Tapping opens the native modal picker. On iOS it renders inline.

Dependencies: @react-native-community/datetimepicker

npx expo install @react-native-community/datetimepicker

Carousel props:

PropTypeNotes
carouselType"normal" | "left-align" | "parallax" | "stack"Layout mode — see below; defaults to "normal"
autoPlaybooleanAuto-advance slides; defaults to false
autoPlayIntervalnumberMilliseconds between auto-advances; defaults to 3000
loopbooleanLoop back to first slide after last; defaults to true
showDotsbooleanShow Pagination.Basic pill dots below carousel; defaults to true
heightnumberSlide height in dp; defaults to 220
widthnumberSlide width; defaults to useWindowDimensions().width (overridden for stack and left-align)
borderRadiusnumberApplied to each slide's inner View
All BaseBoxPropsmargin, padding, alignSelf, borderColor, borderWidth, opacity, etc. applied to outer container

carouselType modes:

ModeWidthLibrary propDescription
"normal"100 % windowFull-width paged carousel
"parallax"100 % windowmode="parallax"Depth-zoom effect on adjacent slides
"stack"75 % windowmode="horizontal-stack"Stacked card effect; multiple slides visible
"left-align"82 % windowNext slide peeks in from the right; overflow: "visible" on container

Slides are arbitrary UIElement trees — place any renderable element (Image, Text, YStack, etc.) as children. Each child becomes one slide.

Dependencies: react-native-reanimated-carousel

npx expo install react-native-reanimated-carousel

Variable context

Input, RadioGroup, CheckboxGroup, and DatePicker elements write into a shared composableVariables map stored in OnboardingProgressContext. This state persists across navigation between ComposableScreen steps, so values collected on an early screen are available on later screens.

Each entry is a structured object { value: string; label?: string }:

Elementvaluelabel
InputRaw text string
RadioGroupSelected item value (e.g. "monthly")Selected item label (e.g. "Monthly")
CheckboxGroupJSON.stringify(string[]) of selected valuesComma-joined display labels (e.g. "Health, Fitness")
DatePickerISO 8601 string (e.g. "1990-01-01T00:00:00.000Z")Locale-formatted date (e.g. "Jan 1, 1990")

Text elements with mode: "expression" interpolate {{variableName}} patterns in their content string from that map. The renderer prefers label over value when both are present. If a key has no value yet, the pattern is replaced with an empty string.

[
{
"id": "name-input",
"type": "Input",
"props": {
"variableName": "name",
"placeholder": "Enter your name",
"autoCapitalize": "words",
"borderRadius": 12
}
},
{
"id": "greeting",
"type": "Text",
"props": {
"content": "Hello {{name}}!",
"mode": "expression",
"fontSize": 18,
"fontWeight": "600",
"textAlign": "center"
}
}
]
note

OnboardingProgressProvider must wrap your app for the variable context to work. If you use @rocapine/react-native-onboarding-ui, wrap your root layout with it:

import { OnboardingProgressProvider } from "@rocapine/react-native-onboarding-ui";

export default function RootLayout() {
return (
<OnboardingProgressProvider>
{/* ... */}
</OnboardingProgressProvider>
);
}

Without the provider the context falls back to its default (empty variables, no-op setter) — Input values will not be saved and expression Text will render blank for any {{variable}}.

Icon props:

PropTypeNotes
namestringRequired — Lucide icon name e.g. "Star", "Heart", "CheckCircle"
sizenumberIcon width & height in dp; defaults to 24
colorstringDefaults to theme.colors.text.primary
strokeWidthnumberLine stroke weight; defaults to 2
width / heightnumberWrapper View dimensions
margin / marginHorizontal / marginVerticalnumber
padding / paddingHorizontal / paddingVerticalnumber
borderWidthnumber
borderRadiusnumber
borderColorstring
opacitynumber

Icon uses Lucide React Native, which is bundled with the UI package — no extra install needed. If an unknown icon name is passed the element renders nothing.

Lottie props:

PropTypeNotes
sourcestringRequired — remote .json URL
widthnumberDefaults to "100%"
heightnumberDefaults to 200
autoPlaybooleanDefaults to true
loopbooleanDefaults to true
speednumberPlayback speed multiplier
opacitynumber
margin / marginHorizontal / marginVerticalnumber
padding / paddingHorizontal / paddingVerticalnumber
borderWidth / borderRadius / borderColornumber | stringApplied via a wrapping View

Dependencies: lottie-react-native

npx expo install lottie-react-native
tip

If lottie-react-native is not installed, the element renders a placeholder view with an install hint instead of crashing.

Rive props:

PropTypeNotes
urlstringRequired — remote .riv URL
widthnumberDefaults to "100%"
heightnumberDefaults to 200
autoplaybooleanDefaults to true
fit"Contain" | "Cover" | "Fill" | "FitWidth" | "FitHeight" | "None" | "ScaleDown" | "Layout"
alignment"TopLeft" | "TopCenter" | "TopRight" | "CenterLeft" | "Center" | "CenterRight" | "BottomLeft" | "BottomCenter" | "BottomRight"
artboardNamestringTarget artboard in the .riv file
stateMachineNamestringState machine to drive
opacitynumber
margin / marginHorizontal / marginVerticalnumber
padding / paddingHorizontal / paddingVerticalnumber
borderWidth / borderRadius / borderColornumber | stringApplied via a wrapping View

Dependencies: rive-react-native

npx expo install rive-react-native
tip

If rive-react-native is not installed, the element renders a placeholder view with an install hint instead of crashing.

Video props:

PropTypeNotes
urlstringRequired — remote video URL
widthnumberDefaults to "100%"
heightnumberDefaults to 200
autoPlaybooleanStart playing on mount; defaults to false
loopbooleanLoop playback; defaults to false
mutedbooleanMute audio; defaults to true (required for autoplay on iOS)
controlsbooleanShow native playback controls; defaults to false
opacitynumber
margin / marginHorizontal / marginVerticalnumber
padding / paddingHorizontal / paddingVerticalnumber
borderWidthnumberApplied via a wrapping View
borderRadiusnumberApplied via a wrapping View
borderColorstringApplied via a wrapping View

Dependencies: expo-video

npx expo install expo-video
tip

If expo-video is not installed, the element renders a placeholder view with an install hint instead of crashing.

Payload:

{
elements: UIElement[]; // Recursive tree of layout and media elements
}

Example:

{
"id": "feature-highlight",
"type": "ComposableScreen",
"payload": {
"elements": [
{
"id": "card",
"type": "YStack",
"props": {
"padding": 24,
"gap": 12,
"borderWidth": 1,
"borderRadius": 16,
"borderColor": "#E0E0E0",
"overflow": "hidden"
},
"children": [
{
"id": "hero",
"type": "Image",
"props": {
"url": "https://example.com/hero.png",
"height": 180,
"resizeMode": "cover",
"borderRadius": 12
}
},
{
"id": "title",
"type": "Text",
"props": {
"content": "Welcome aboard",
"fontSize": 24,
"fontWeight": "700"
}
},
{
"id": "row",
"type": "XStack",
"props": { "gap": 8, "alignItems": "center" },
"children": [
{
"id": "badge",
"type": "YStack",
"props": {
"backgroundColor": "#E8F5E9",
"borderRadius": 8,
"padding": 8
},
"children": [
{
"id": "badge-text",
"type": "Text",
"props": { "content": "New", "fontSize": 12, "color": "#2E7D32" }
}
]
},
{
"id": "label",
"type": "Text",
"props": { "content": "Personalized for you", "fontSize": 14 }
}
]
}
]
}
]
}
}

ZStack example — image with text overlay:

{
"id": "hero-overlay",
"type": "ZStack",
"props": {
"height": 200,
"borderRadius": 16,
"overflow": "hidden",
"marginVertical": 8
},
"children": [
{
"id": "bg",
"type": "Image",
"props": { "url": "https://example.com/hero.jpg", "height": 200, "resizeMode": "cover" }
},
{
"id": "overlay",
"type": "YStack",
"props": { "backgroundColor": "rgba(0,0,0,0.45)", "padding": 20, "justifyContent": "flex-end" },
"children": [
{
"id": "label",
"type": "Text",
"props": { "content": "Your headline here", "fontSize": 18, "fontWeight": "700", "color": "#fff" }
}
]
}
]
}

Dependencies: None for YStack, XStack, ZStack, Text, Image, Icon, Input, RadioGroup, CheckboxGroup, Button. SafeAreaView requires react-native-safe-area-context (already a peer dep of @rocapine/react-native-onboarding-ui). See the Carousel, Lottie, Rive, Video, and DatePicker sections above for elements with peer deps.


Common Properties

All page types share these properties:

{
id: string; // Unique identifier
type: string; // Discriminated union field
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
}

Customization Levels

Each page type supports three levels of customization:

Level 1: Theming

Apply theme tokens to all screens:

<OnboardingProvider
theme={{
colors: { primary: "#FF5733" },
typography: { fontFamily: { title: "CustomFont" } }
}}
/>

Learn more about theming →

Level 2: Custom Components

Replace specific UI components (currently available for Question type):

<OnboardingProvider
customComponents={{
QuestionAnswerButton: MyCustomButton
}}
/>

Learn more about custom components →

Level 3: Custom Renderers

Complete control over specific screens:

// In your routing file
if (step.id === "custom-screen") {
return <MyCustomRenderer step={step} onContinue={onContinue} />;
}

Learn more about custom renderers →


Media Support

Several page types support media with the MediaSourceSchema:

{
type: "video" | "image" | "lottie" | "rive";
url?: string; // Remote URL
localPathId?: string; // Local asset path
}

Supported media types:

  • video: Video files (implementation pending)
  • image: PNG, JPG, WebP via React Native Image
  • lottie: Lottie animations via lottie-react-native
  • rive: Rive animations via rive-react-native

Using Individual Renderers

You can use renderers directly without OnboardingPage:

import { MediaContentRenderer, MediaContentStepType } from "@rocapine/react-native-onboarding";

const step: MediaContentStepType = {
id: "welcome",
type: "MediaContent",
name: "Welcome",
displayProgressHeader: true,
payload: {
title: "Welcome!",
description: "Let's get started",
media: { type: "image", url: "https://..." },
},
customPayload: null,
continueButtonLabel: "Get Started",
figmaUrl: null,
};

<MediaContentRenderer step={step} onContinue={handleContinue} />

Next Steps