Beautiful, customizable onboarding flows for Expo React Native apps with smooth animations and video support.
- π Modal-based - Non-intrusive onboarding experience
- π¨ Beautiful animations - Fade, slide, and scale effects
- π₯ Image & Video support - Use images or MP4 videos
- π± TypeScript first - Full type safety
- π― Minimal dependencies - Only expo-av for videos
- π± Expo compatible - Designed for Expo React Native projects
- πͺ Highly customizable - Control every aspect
- π Progress indicators - Visual progress dots
- π Closeable control - Prevent/allow closing
- π° Paywall support - Display custom paywall after onboarding
demo-package.mp4
- Expo React Native app (uses
expo-avfor video support) - React Native >= 0.60.0
- Expo SDK >= 49.0.0
Install the package along with its peer dependency:
npm install react-native-onboarding-flow expo-av
# or
yarn add react-native-onboarding-flow expo-avWhy install expo-av? It's a peer dependency needed for video playback. This prevents version conflicts and keeps your bundle size optimized.
import React, { useState } from 'react';
import { OnboardingFlow } from 'react-native-onboarding-flow';
const App = () => {
const [showOnboarding, setShowOnboarding] = useState(true);
const slides = [
{
id: 'welcome',
media: {
type: 'video',
source: require('./assets/welcome-video.mp4'),
autoPlay: true,
loop: true,
muted: true,
},
title: 'Welcome to Our App',
description: 'Experience the future of mobile apps',
animation: 'scaleIn',
},
{
id: 'features',
media: {
type: 'image',
source: require('./assets/features.png'),
width: 300,
height: 200,
borderRadius: 20,
},
title: 'Powerful Features',
description: 'Everything you need in one place',
animation: 'fadeIn',
},
];
return (
<OnboardingFlow
slides={slides}
visible={showOnboarding}
onComplete={() => setShowOnboarding(false)}
closeable={false}
showProgress={true}
/>
);
};Display a custom paywall component after the onboarding completes:
import React, { useState } from 'react';
import { View, Text, TouchableOpacity } from 'react-native';
import { OnboardingFlow } from 'react-native-onboarding-flow';
const MyPaywall = ({ onComplete }) => (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center', padding: 20 }}>
<Text style={{ fontSize: 24, fontWeight: 'bold', marginBottom: 20 }}>
Unlock Premium Features
</Text>
<Text style={{ fontSize: 16, textAlign: 'center', marginBottom: 30 }}>
Get access to all features with our premium subscription
</Text>
<TouchableOpacity
style={{ backgroundColor: '#007AFF', padding: 15, borderRadius: 25 }}
onPress={onComplete} // This will close the onboarding flow
>
<Text style={{ color: 'white', fontSize: 16 }}>Start Free Trial</Text>
</TouchableOpacity>
</View>
);
const App = () => {
const [showOnboarding, setShowOnboarding] = useState(true);
return (
<OnboardingFlow
slides={slides}
visible={showOnboarding}
onComplete={() => setShowOnboarding(false)}
showPaywall={true}
paywallComponent={<MyPaywall />}
/>
);
};const App = () => {
const [showOnboarding, setShowOnboarding] = useState(true);
// Custom theme colors
const theme = {
backgroundColor: '#1a1a1a',
titleColor: '#ffffff',
descriptionColor: '#cccccc',
buttonBackgroundColor: '#007AFF',
buttonTextColor: '#ffffff',
progressDotColor: '#333333',
progressDotActiveColor: '#007AFF',
closeButtonColor: '#cccccc',
};
return (
<OnboardingFlow
slides={slides}
visible={showOnboarding}
onComplete={() => setShowOnboarding(false)}
theme={theme}
/>
);
};| Prop | Type | Default | Description |
|---|---|---|---|
slides |
OnboardingSlideData[] |
required | Array of slide data |
visible |
boolean |
required | Controls modal visibility |
onComplete |
() => void |
required | Called when onboarding completes |
closeable |
boolean |
false |
Allow users to close before completion |
showProgress |
boolean |
true |
Show progress dots |
theme |
OnboardingTheme |
undefined |
Custom colors |
showPaywall |
boolean |
false |
Show paywall after last slide |
paywallComponent |
React.ReactNode |
undefined |
Custom paywall component |
interface OnboardingSlideData {
id: string;
media: {
type: 'image' | 'video';
source: any; // require() asset
autoPlay?: boolean; // For videos (default: true)
loop?: boolean; // For videos (default: true)
muted?: boolean; // For videos (default: true)
width?: number; // Custom width in pixels
height?: number; // Custom height in pixels
borderRadius?: number; // Custom border radius in pixels
};
title: string;
description: string;
animation?: 'fadeIn' | 'slideUp' | 'scaleIn';
}| Option | Type | Default | Description |
|---|---|---|---|
type |
'image' | 'video' |
required | Media type |
source |
any |
required | Asset source (require()) |
autoPlay |
boolean |
true |
Auto-play videos when slide becomes active |
loop |
boolean |
true |
Loop videos continuously |
muted |
boolean |
true |
Start videos muted |
width |
number |
screenWidth * 0.6 |
Custom width in pixels (default: 60% of screen width) |
height |
number |
screenWidth * 0.6 |
Custom height in pixels (default: 60% of screen width) |
borderRadius |
number |
12 (videos), undefined (images) |
Border radius in pixels |
| Option | Type | Default | Description |
|---|---|---|---|
backgroundColor |
string |
'white' |
Modal background color |
titleColor |
string |
'#2F4F2F' |
Title text color |
descriptionColor |
string |
'#666' |
Description text color |
buttonBackgroundColor |
string |
'#6B8E5A' |
Next button background |
buttonTextColor |
string |
'white' |
Next button text color |
progressDotColor |
string |
'#E5E5E5' |
Inactive dot color |
progressDotActiveColor |
string |
'#6B8E5A' |
Active dot color |
closeButtonColor |
string |
'#666' |
Close button icon color |
fadeIn- Gentle fade in effectslideUp- Slide up from bottomscaleIn- Scale in with spring animation
MIT Β© Binb1