Building User Onboarding Flows
Create dynamic, updatable onboarding experiences for your mobile app.
Overview
Learn how to build user onboarding flows that you can update and iterate on without app store reviews.
What You'll Build
A multi-screen onboarding flow with:
- Welcome screen with app highlights
- Feature showcase carousel
- User information collection form
- Permissions requests
- Success screen
Step 1: Design Onboarding Screens
In the Resync Dashboard:
- Create Content Blocks:
Screen 1: Welcome
Content Block Name: OnboardingWelcome
Elements:
- Hero image
- Heading: "Welcome to [App Name]"
- Subheading: "Ship faster, code less"
- Button: "Get Started" → navigates to next screen
Screen 2: Features
Content Block Name: OnboardingFeatures
Elements:
- Carousel/List of feature cards
- Each card: Icon + Title + Description
- Button: "Continue" → navigates to next screen
Screen 3: Account Setup
Content Block Name: OnboardingAccountSetup
Form Elements:
- Text input: Full Name
- Email input: Email Address
- Select: Country
- Checkbox: "I agree to Terms of Service"
- Submit button: "Create Account"
Step 2: Implement Navigation
React Native Example
import React from 'react';
import { createNativeStackNavigator } from '@react-navigation/native-stack';
import { ResyncContentView } from 'resync-react-native';
const OnboardingStack = createNativeStackNavigator();
export default function OnboardingNavigator() {
return (
<OnboardingStack.Navigator screenOptions={{ headerShown: false }}>
<OnboardingStack.Screen
name="Welcome"
component={WelcomeScreen}
/>
<OnboardingStack.Screen
name="Features"
component={FeaturesScreen}
/>
<OnboardingStack.Screen
name="AccountSetup"
component={AccountSetupScreen}
/>
</OnboardingStack.Navigator>
);
}
Step 3: Implement Each Screen
Welcome Screen
import { useNavigation } from '@react-navigation/native';
import { ResyncContentView } from 'resync-react-native';
function WelcomeScreen() {
const navigation = useNavigation();
const navigationRegistry = {
navigate: (screen) => navigation.navigate(screen),
goBack: () => navigation.goBack(),
};
return (
<ResyncContentView
name="OnboardingWelcome"
navigationRegistry={navigationRegistry}
/>
);
}
Features Screen
function FeaturesScreen() {
const navigation = useNavigation();
const navigationRegistry = {
navigate: (screen) => navigation.navigate(screen),
goBack: () => navigation.goBack(),
};
const functionRegistry = {
onContinue: () => {
// Track feature view
Resync.logEvent({
eventId: 'evt_onboarding_features_viewed',
});
navigation.navigate('AccountSetup');
},
};
return (
<ResyncContentView
name="OnboardingFeatures"
navigationRegistry={navigationRegistry}
functionRegistry={functionRegistry}
/>
);
}
Account Setup Screen
function AccountSetupScreen() {
const navigation = useNavigation();
const functionRegistry = {
handleAccountSetup: async (formData) => {
try {
// Create account
const user = await createAccount(formData);
// Set user in Resync
await Resync.loginUser(user.id, {
email: formData.email,
name: formData.name,
});
// Track completion
Resync.logEvent({
eventId: 'evt_onboarding_completed',
});
// Navigate to app
navigation.replace('Home');
} catch (error) {
console.error('Account setup failed:', error);
}
},
};
return (
<ResyncContentView
name="OnboardingAccountSetup"
functionRegistry={functionRegistry}
/>
);
}
Step 4: Track Onboarding Progress
import AsyncStorage from '@react-native-async-storage/async-storage';
// Check if user has completed onboarding
async function hasCompletedOnboarding() {
const completed = await AsyncStorage.getItem('onboarding_completed');
return completed === 'true';
}
// Mark onboarding as complete
async function markOnboardingComplete() {
await AsyncStorage.setItem('onboarding_completed', 'true');
Resync.logEvent({
eventId: 'evt_onboarding_completed',
});
}
// In your App component
function App() {
const [showOnboarding, setShowOnboarding] = useState(false);
const [loading, setLoading] = useState(true);
useEffect(() => {
checkOnboarding();
}, []);
const checkOnboarding = async () => {
const completed = await hasCompletedOnboarding();
setShowOnboarding(!completed);
setLoading(false);
};
if (loading) {
return <LoadingScreen />;
}
return showOnboarding ? <OnboardingNavigator /> : <MainApp />;
}
Step 5: A/B Test Your Onboarding
Test different onboarding flows:
import { ResyncCampaignView } from 'resync-react-native';
import Resync from 'resync-react-native';
function OnboardingNavigator() {
const handleOnboardingComplete = async () => {
await Resync.logEvent({
eventId: 'evt_onboarding_completed',
});
await markOnboardingComplete();
navigation.replace('Home');
};
const functionRegistry = {
onComplete: handleOnboardingComplete,
};
// ResyncCampaignView automatically handles variant assignment
return (
<ResyncCampaignView
name="onboarding_test"
functionRegistry={functionRegistry}
/>
);
}
That's all you need! Create different onboarding variants in your dashboard, and ResyncCampaignView will handle the rest.
Step 6: Update Without Releases
Change onboarding content from the dashboard:
- Go to Content Blocks → OnboardingWelcome
- Update the heading, images, or button text
- Click Publish
- Changes appear in your app instantly!
Best Practices
1. Keep It Short
- 3-5 screens maximum
- Focus on core value proposition
- Defer complex setup to later
2. Track Everything
// Track screen views
Resync.logEvent({
eventId: 'evt_onboarding_screen_viewed',
metadata: { screen: 'welcome', step: 1 },
});
// Track interactions
Resync.logEvent({
eventId: 'evt_onboarding_button_clicked',
metadata: { button: 'get_started' },
});
// Track completion
Resync.logEvent({
eventId: 'evt_onboarding_completed',
metadata: { completedAt: Date.now() },
});
3. Measure Success
Track key metrics:
- Completion rate
- Time to complete
- Drop-off points
- User satisfaction