Skip to main content

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:

  1. 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:

  1. Go to Content BlocksOnboardingWelcome
  2. Update the heading, images, or button text
  3. Click Publish
  4. 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

Next Steps