Building Effective Flows
Best practices for designing flows that engage users and generate valuable insights.
Introduction#
Flows are full-screen HTML experiences displayed inside a WebView. They're perfect for onboarding, permission requests, feature announcements, surveys, and upgrade prompts.
Well-designed flows guide users through important moments while generating actionable analytics. This guide covers the principles and patterns that make flows effective.
Flow Structure#
Every flow is a single HTML file with embedded CSS and JavaScript:
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<style>
/* Your CSS here */
</style>
</head>
<body>
<!-- Your HTML content -->
<script>
// Your JavaScript here
// Mobana bridge is available as window.Mobana
</script>
</body>
</html>The Mobana bridge is injected before your code runs, so you can use it immediately in your scripts.
Design Principles#
1. Keep It Focused#
Each flow should have one clear goal. Don't try to do too much.
- ✓Onboarding flow: Explain value, collect preferences
- ✓Permission flow: Explain why, request one permission
- ✗Mega flow: Onboarding + permissions + upsell + survey
2. Lead with Value#
Before asking for anything (permission, signup, purchase), explain what the user gets.
- ✓"Get daily tips to help you reach your goals" → Enable notifications
- ✗"Enable notifications" → (no context)
3. Always Provide an Exit#
Users should always be able to dismiss or skip. Trapped users become frustrated users.
- • Include a "Skip" or "Maybe later" option
- • Or a visible close/dismiss button
- • Track skips as events for insights
4. Show Progress#
For multi-step flows, show users where they are and how many steps remain.
- • Step indicators (dots, numbers)
- • Progress bars
- • "Step 2 of 4" text
5. Provide Feedback#
Use haptics and visual feedback to acknowledge user actions.
// On button tap
Mobana.haptic('light');
// On success
Mobana.haptic('success');
// On error
Mobana.haptic('error');Common Flow Patterns#
Multi-Step Onboarding#
A classic onboarding flow with step indicator:
<!-- Step indicator -->
<div class="step-indicator">
<div class="step active" data-step="1"></div>
<div class="step" data-step="2"></div>
<div class="step" data-step="3"></div>
</div>
<!-- Step content -->
<div class="step-content" id="step-1">
<h1>Welcome to MyApp!</h1>
<p>Let's get you set up in just a few steps.</p>
<button id="step-1-continue">Continue</button>
</div>
<script>
let currentStep = 1;
document.getElementById('step-1-continue').addEventListener('click', () => {
Mobana.trackEvent('step_1_completed');
Mobana.haptic('light');
showStep(2);
});
function showStep(step) {
// Hide all steps, show target step
document.querySelectorAll('.step-content').forEach(el => el.style.display = 'none');
document.getElementById(`step-${step}`).style.display = 'block';
// Update indicator
document.querySelectorAll('.step').forEach(el => {
el.classList.toggle('active', parseInt(el.dataset.step) <= step);
});
currentStep = step;
}
</script>Permission Request#
The "pre-permission" pattern — explain value before triggering the system dialog:
<!-- Permission request pattern -->
<div class="permission-screen">
<div class="icon">🔔</div>
<h2>Stay in the loop</h2>
<p>Get notified about important updates and special offers.</p>
<!-- Explain the value before asking -->
<ul class="benefits">
<li>Daily tips to help you succeed</li>
<li>Exclusive offers just for you</li>
<li>Important account updates</li>
</ul>
<button id="enable-btn" class="primary">Enable Notifications</button>
<button id="skip-btn" class="secondary">Maybe Later</button>
</div>
<script>
document.getElementById('enable-btn').addEventListener('click', async () => {
Mobana.trackEvent('notification_prompt_accepted');
const granted = await Mobana.requestNotificationPermission();
if (granted) {
Mobana.trackEvent('notification_permission_granted');
Mobana.haptic('success');
} else {
Mobana.trackEvent('notification_permission_denied');
}
// Continue regardless of result
showNextScreen();
});
document.getElementById('skip-btn').addEventListener('click', () => {
Mobana.trackEvent('notification_prompt_skipped');
showNextScreen();
});
</script>This pattern significantly increases permission grant rates because users understand the value before seeing the system dialog.
Personalization#
Use params and attribution to customize the flow:
<script>
// Get params passed from the app
const params = Mobana.getParams();
const attribution = Mobana.getAttribution();
// Personalize greeting
const greeting = document.getElementById('greeting');
if (params.userName) {
greeting.textContent = `Welcome, ${params.userName}!`;
} else {
greeting.textContent = 'Welcome!';
}
// Show campaign-specific content
if (attribution?.utm_campaign === 'premium_promo') {
document.getElementById('premium-offer').style.display = 'block';
}
// Adapt for platform
if (Mobana.getPlatform() === 'ios') {
document.getElementById('ios-specific').style.display = 'block';
}
// Respect color scheme
const colorScheme = Mobana.getColorScheme();
document.body.classList.add(`theme-${colorScheme}`);
</script>Testing Flows#
- Preview in Dashboard: Use the flow editor's preview to test on web first.
- Test on real devices: WebView behavior can differ from desktop browsers.
- Test both platforms: Check iOS and Android for safe area differences.
- Test dark mode: Toggle device color scheme and verify your flow adapts.
- Test permissions: Test with permissions already granted, denied, and blocked.
Performance Tips#
- Prefetch flows: Call prefetchFlow() during app startup for instant display.
- Optimize images: Use compressed images and appropriate sizes.
- Inline critical CSS: Include styles in the HTML to avoid render blocking.
- Minimize JavaScript: Keep scripts lean; the bridge provides most functionality.
Next Steps#
- Flow Events Tracking — Learn what flow events to track for insights
- Flow Bridge API — Full reference for all bridge methods
- CSS Variables — Safe areas, screen dimensions, color scheme