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 { ResyncContentBlock } 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 { ResyncContentBlock } from 'resync-react-native';

function WelcomeScreen() {
const navigation = useNavigation();

const navigationRegistry = {
navigate: (screen) => navigation.navigate(screen),
goBack: () => navigation.goBack(),
};

return (
<ResyncContentBlock
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 (
<ResyncContentBlock
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 (
<ResyncContentBlock
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 Blocks → OnboardingWelcome
  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​