Onboarding Flow

A streamlined, intuitive process that guides users through account setup while eliminating friction points in the onboarding experience.

ReactTypeScriptNode.jsSQLPythonAPI Design

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:

1

User Enters
Platform

2

Modal
Appears

3

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.

Duolingo onboarding step 1

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 NameTypeDescription
idUUIDPrimary key (existing)
first_nametextUser's first name (added)
last_nametextUser's last name (added)
phone_numbertextUser's contact number (added)
position_typeposition_t (ENUM)User's role at the property (added)
referral_sourcesource_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

GET/api/profiles/current
PATCH/api/profiles/update
GET/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_errorForm validation failures
  • auth_errorAuthentication issues
  • not_foundResource 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

1
Planning (1 week)
  • Requirements gathering
  • User flow design
  • Technical architecture
2
Development (2 weeks)
  • Frontend components
  • Backend API
  • Database schema
3
Testing (1 week)
  • Component testing
  • End-to-end validation
  • User acceptance
4
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