Onboarding Flow
A streamlined, intuitive process that guides users through account setup while eliminating friction points in the onboarding experience.
40%
Completion Rate Increase
35%
Support Ticket Reduction
25s
Average Completion Time
Problem Statement
The platform lacked a structured onboarding process to capture essential user information, leading to incomplete profiles and operational inefficiencies. Without this data, user experience was fragmented, personalization was limited, and key operational insights were missing.
A software solution was required to introduce an automated onboarding modal that would prompt users to complete their profiles in a seamless, non-intrusive manner. This would ensure data consistency, reduce manual follow-ups, and enhance engagement from the start.
If Not Addressed:
- •Users would face an impersonal and disjointed experience.
- •Manual efforts would be required to collect missing information, causing inefficiencies.
- •Business insights would suffer due to incomplete data, impacting decision-making and overall platform effectiveness.
Project Goals:
New users should be prompted to provide the following information in a pop-up modal upon entering the web application:
- •How they heard about the platform
- •First and last name
- •Phone number
- •Position at the property
This information should be persisted in the SQL data store, with an emphasis on user convenience, minimal clicking, and efficient data entry.
Process Flow:
User Enters
Platform
Modal
Appears
Profile
Completed
High-Level Approach
This project implements a dynamic onboarding modal that ensures essential user information is collected efficiently and seamlessly. The solution is structured as follows:
Automated Triggering Mechanism
- •The system checks for missing profile data when a user logs in.
- •If required fields are incomplete, the onboarding modal automatically appears.
Step-by-Step Guided Process
- •The modal guides users through a structured, multi-step flow to provide missing details.
- •It opens at the specific step where the first missing field is detected.
Seamless User Experience
- •The UI matches the platform's aesthetics, ensuring a cohesive look.
- •Users can complete the onboarding in a non-intrusive way, minimizing friction.
Data Validation & Submission
- •Inputs are validated in real time to ensure accuracy and completeness.
- •Data is securely stored in the database upon submission.
Dismissal Logic
The modal will only disappear once all required fields are completed, ensuring comprehensive data collection while respecting user experience.
Design Inspiration
Duolingo's Onboarding Flow
The feature was inspired by Duolingo's intuitive step-by-step onboarding process, which effectively guides users through providing information in a gamified, low-friction manner. Below are screenshots of Duolingo's approach that influenced our design decisions.

Step 1: Initial welcome screen
These design patterns were adapted to our specific use case, focusing on minimizing friction while ensuring all necessary user information was collected in a structured, engaging way.
Technical Implementation
Database Schema
This feature collects user information that is organized in one database table: profiles
. The pre-existing profiles table already included authenticated user information, so we added new columns to store the additional user-provided information.
Column Name | Type | Description |
---|---|---|
id | UUID | Primary key (existing) |
first_name | text | User's first name (added) |
last_name | text | User's last name (added) |
phone_number | text | User's contact number (added) |
position_type | position_t (ENUM) | User's role at the property (added) |
referral_source | source_type (ENUM) | How the user heard about the platform (added) |
Design Decision: ENUM vs. Separate Tables
Initial Approach: Separate Tables
Initially, we considered creating separate tables for positions to maintain normalization and support future scalability:
CREATE TABLE positions ( position_id UUID PRIMARY KEY, position_name VARCHAR(255) NOT NULL ); ALTER TABLE profiles ADD COLUMN position UUID REFERENCES positions(position_id);
This approach would have maintained normalization but introduced unnecessary complexity given the limited number of predefined positions.
Revised Approach: Using ENUMs
After evaluation, we decided to use ENUM fields instead of separate tables, providing several benefits:
Limited Sets of Values
- Positions were predefined and rarely changed
- No need for dynamic position management
Simplified Queries
- Eliminated the need for JOIN operations
- Reduced query complexity and execution time
Better Performance
- Direct value access without requiring additional table lookups
- Reduced database storage by eliminating extra tables and foreign keys
Easier Maintenance
- No need to manage separate tables
- Simpler codebase and data model
- Type safety enforced at the database level
Final Implementation
ALTER TABLE profiles ADD COLUMN position_type ENUM('owner', 'manager', 'team_member', 'front_desk_manager', 'receptionist', 'maintenance_staff', 'housekeeping_staff') NOT NULL; ALTER TABLE profiles ADD COLUMN referral_source ENUM('google_search', 'facebook_instagram', 'tiktok', 'youtube', 'tv', 'news_article_blog', 'friends_family', 'other') NOT NULL;
Database Optimizations
Strategic Indexing
We added targeted indexes to improve query performance for onboarding-related operations:
-- Composite index for filtering incomplete profiles CREATE INDEX idx_profiles_completion_status ON profiles(referral_source, position_type) WHERE referral_source IS NULL OR position_type IS NULL;
This optimization reduced onboarding status check query time from 120ms to under 30ms.
Data Validation
Database-level constraints ensure data integrity regardless of which client interacts with the database:
-- Add validation constraints ALTER TABLE profiles ADD CONSTRAINT valid_phone_format CHECK (phone_number ~ '^+?[0-9]{10,15}$');
This reduced the need for duplicate validation logic in the application layer.
Migration Strategy
For existing users, we implemented a careful migration approach using Supabase migrations:
Safe Schema Updates
ALTER TABLE profiles ADD COLUMN IF NOT EXISTS referral_source VARCHAR(100) DEFAULT NULL, ADD COLUMN IF NOT EXISTS position_type VARCHAR(50) DEFAULT NULL;
Version Control
# Generate and apply migration npx supabase migration new add_onboarding_fields npx supabase db push
Each migration was tested in development and staging environments before being applied to production during scheduled maintenance windows.
APIs and Interfaces
The onboarding flow is powered by a set of RESTful APIs that handle user profile data management, following principles of progressive disclosure and idempotency.
Core Endpoints
/api/profiles/current
/api/profiles/update
/api/profiles/onboarding-status
API Design Principles
- •RESTful Architecture
with standard HTTP methods and status codes
- •Progressive Disclosure
supporting partial updates for step-by-step completion
- •Idempotent Operations
preventing duplicate submissions
- •Minimal Payload
transmitting only required fields
Request/Response Example
Profile Update Request:
// PATCH /api/profiles/update { "referral_source": "google", "first_name": "John", "last_name": "Smith", "phone_number": "1234567890", "position_type": "owner" }
Response:
{ "success": true, "data": { "id": "user-uuid", "first_name": "John", "last_name": "Smith", "phone_number": "1234567890", "position_type": "owner", "referral_source": "google", "updated_at": "2023-10-15T14:22:33Z" } }
Error Handling
Standardized error format:
{ "success": false, "error": { "code": "validation_error", "message": "Phone number must be 10 digits", "details": { "field": "phone_number", "constraint": "length" } } }
Consistent error types:
validation_error
Form validation failuresauth_error
Authentication issuesnot_found
Resource not found
Security Considerations
Input Protection
- •Server-side validation
- •Data sanitization
- •Type checking
Access Control
- •Rate limiting (10 req/min)
- •CORS restrictions
- •Authentication checks
Monitoring
- •Audit logging
- •Change tracking
- •Error monitoring
Frontend Implementation
Component Architecture
The onboarding modal follows a composable architecture with independent, reusable components:
OnboardingModal (container) ├── ProgressBar ├── QuestionStep (for each step) │ ├── SourceSelector (step 1) │ ├── NameInput (step 2) │ ├── PhoneInput (step 3) │ └── PositionSelector (step 4) └── NavigationButtons
This modular approach enables independent testing, reusability, and easier maintenance.
State Management
A multi-layered state management approach handles form data, validation, and navigation:
- •Local Form State
React useState for current step values and validation
- •Persistent Storage
Saves progress after each step completion
- •Server Synchronization
Updates profile in real-time as users progress
- •Non-Linear Navigation
Tracks visited steps to allow back/forward movement
User Experience Enhancements
Progressive Validation
Real-time feedback as users type with field-specific guidance
Visual Progress
Clear indicators show completion status throughout the flow
Smart Defaults
Pre-filled fields with intelligent suggestions when possible
Optimistic UI
Updates UI before server confirmation for responsiveness
Accessibility Considerations
Keyboard Navigation
Full keyboard support with optimized tab order
Screen Readers
ARIA labels and roles for all interactive elements
Focus Management
Proper focus handling when steps change
Color Contrast
All text meets WCAG AA standards for readability
Performance Optimizations
Lazy Loading
Modal components are loaded only when needed, reducing initial bundle size
Debounced Validation
Input validation is debounced to prevent excessive re-renders during typing
Memoized Components
React.memo for complex components prevents unnecessary re-renders
Example of debounced validation:
// Debounced validation example const debouncedValidate = useCallback( debounce((value: string, field: string) => { validateField(value, field); }, 300), [] );
Logging and Data Production
To measure effectiveness and identify improvement opportunities, I implemented a dual-layer logging approach that captured both client-side user interactions and server-side processing events.
Client-side Tracking
User interactions were tracked to measure engagement and identify friction points in the onboarding flow.
const handleStepCompletion = (step: number) => { // Log step completion console.info(`User completed onboarding step ${step+1}/${questions.length}`); analytics.track('onboarding_step_completed', { step: step+1, total_steps: questions.length, time_spent: Date.now() - stepStartTime }); };
Key Metrics Captured:
- •Time spent on each step
- •Drop-off points in the flow
- •Field error rates
Server-side Logging
Structured server logs captured detailed information about profile updates and potential errors.
async def update_onboarding_profile(user_id: str, request: OnboardingProfileRequest): try: # Update profile logic... log.info(f"User {user_id} completed onboarding", extra={ "referral_source": request.source_type, "has_phone": bool(request.phone_number), "position_type": request.position_type }) except Exception as e: log.error(f"Error updating onboarding profile: {str(e)}")
Data Captured:
- •User progression through steps
- •Validation failures with field details
- •API response times and errors
Data Storage & Analysis
Completion metrics were stored directly in the database for long-term analysis and reporting.
-- Added tracking columns to profiles table ALTER TABLE profiles ADD COLUMN onboarding_started_at TIMESTAMP, ADD COLUMN onboarding_completed_at TIMESTAMP;
Key Insights Gained
- •Completion rate across user segments
- •Average time-to-complete
- •Most common referral sources
Actionable Outcomes
- •Identified and simplified problematic steps
- •Improved field validation based on error patterns
- •Optimized step order for better flow
Project Management
This project followed a structured development approach with clear milestones and comprehensive testing to ensure successful delivery.
Milestones
Planning (1 week)
- •Requirements gathering
- •User flow design
- •Technical architecture
Development (2 weeks)
- •Frontend components
- •Backend API
- •Database schema
Testing (1 week)
- •Component testing
- •End-to-end validation
- •User acceptance
Deployment (3 days)
- •Staged rollout
- •Monitoring
- •Documentation
Testing Strategy
- •Component Testing: Jest for React components
- •Integration Testing: API validation with Postman
- •User Testing: Sessions with 5 users
- •Coverage: Critical paths prioritized
Frontend Safeguards
- •Implement retry logic for failed API calls
- •Cache form data locally to prevent loss
- •Allow manual form submission as a fallback
- •Provide skip/save-for-later options
Backend Protection
- •Rate limiting on API endpoints
- •Database connection pooling
- •Query timeout handling
- •Fallback to minimal data collection
Recovery Mechanisms
- •Auto-save progress every step
- •Resume capability from the last completed step
- •Manual override options for the support team
- •Accessibility features for keyboard navigation