# User Onboarding Flows **Meta-Description:** User Onboarding für SaaS optimieren. Progressive Onboarding, Checklists, Product Tours und Activation Metrics. **Keywords:** User Onboarding, Activation, Product Tour, Checklist, Time-to-Value, Retention, SaaS Onboarding --- ## Einführung **User Onboarding** entscheidet über Trial-to-Paid Conversion und Retention. Der erste Eindruck zählt – **40-60% der Free Trial User** kehren nach dem ersten Tag nie zurück. Dieser Guide zeigt effektive Onboarding-Strategien. --- ## Onboarding Overview ``` ┌─────────────────────────────────────────────────────────────┐ │ USER ONBOARDING JOURNEY │ ├─────────────────────────────────────────────────────────────┤ │ │ │ Onboarding Phases: │ │ ┌─────────────────────────────────────────────────────┐ │ │ │ 1. SIGNUP FLOW: │ │ │ │ ├── Minimal friction │ │ │ │ ├── Social login options │ │ │ │ └── Welcome message │ │ │ │ ↓ │ │ │ │ 2. INITIAL SETUP: │ │ │ │ ├── Profile completion │ │ │ │ ├── Preferences selection │ │ │ │ └── Team invitation (if B2B) │ │ │ │ ↓ │ │ │ │ 3. FIRST VALUE: │ │ │ │ ├── Guided first action │ │ │ │ ├── Quick win celebration │ │ │ │ └── "Aha moment" trigger │ │ │ │ ↓ │ │ │ │ 4. ACTIVATION: │ │ │ │ ├── Core features used │ │ │ │ ├── Habit formation │ │ │ │ └── Ready for conversion │ │ │ └─────────────────────────────────────────────────────┘ │ │ │ │ Key Metrics: │ │ ├── Time to First Value (TTFV) │ │ ├── Activation Rate │ │ ├── Onboarding Completion Rate │ │ ├── Day 1/7/30 Retention │ │ └── Trial-to-Paid Conversion │ │ │ │ Activation Events (Example): │ │ ├── Created first project │ │ ├── Invited team member │ │ ├── Connected integration │ │ └── Completed first workflow │ │ │ └─────────────────────────────────────────────────────────────┘ ``` --- ## Onboarding Checklist ```typescript // lib/onboarding/types.ts export interface OnboardingStep { id: string; title: string; description: string; action: { type: 'link' | 'modal' | 'trigger'; target: string; }; completionEvent: string; points?: number; required?: boolean; order: number; } export interface UserOnboarding { userId: string; completedSteps: string[]; currentStep: string | null; startedAt: Date; completedAt: Date | null; skippedAt: Date | null; } // lib/onboarding/config.ts export const onboardingSteps: OnboardingStep[] = [ { id: 'complete-profile', title: 'Complete your profile', description: 'Add your name and profile picture', action: { type: 'link', target: '/settings/profile' }, completionEvent: 'profile_completed', points: 10, required: true, order: 1 }, { id: 'create-project', title: 'Create your first project', description: 'Set up a project to organize your work', action: { type: 'modal', target: 'create-project-modal' }, completionEvent: 'project_created', points: 20, required: true, order: 2 }, { id: 'invite-team', title: 'Invite your team', description: 'Collaborate with your colleagues', action: { type: 'link', target: '/settings/team' }, completionEvent: 'team_member_invited', points: 15, required: false, order: 3 }, { id: 'connect-integration', title: 'Connect an integration', description: 'Sync with your favorite tools', action: { type: 'link', target: '/settings/integrations' }, completionEvent: 'integration_connected', points: 20, required: false, order: 4 }, { id: 'complete-workflow', title: 'Complete your first workflow', description: 'Experience the full power of our platform', action: { type: 'trigger', target: 'start-workflow-tour' }, completionEvent: 'workflow_completed', points: 35, required: true, order: 5 } ]; export const ACTIVATION_THRESHOLD = 3; // Steps to be "activated" ``` ```typescript // lib/onboarding/service.ts import { db } from '@/lib/db'; import { onboardingSteps, ACTIVATION_THRESHOLD } from './config'; import { track } from '@/lib/analytics'; export async function getOnboardingProgress(userId: string) { const onboarding = await db.userOnboarding.findUnique({ where: { userId } }); if (!onboarding) { // Initialize onboarding return db.userOnboarding.create({ data: { userId, completedSteps: [], currentStep: onboardingSteps[0].id, startedAt: new Date() } }); } return onboarding; } export async function completeOnboardingStep( userId: string, stepId: string ): Promise<{ completed: boolean; isActivated: boolean; nextStep: string | null }> { const onboarding = await getOnboardingProgress(userId); if (onboarding.completedSteps.includes(stepId)) { return { completed: false, isActivated: isUserActivated(onboarding.completedSteps), nextStep: onboarding.currentStep }; } const step = onboardingSteps.find(s => s.id === stepId); if (!step) { throw new Error(`Unknown step: ${stepId}`); } const completedSteps = [...onboarding.completedSteps, stepId]; const nextStep = getNextStep(completedSteps); const isCompleted = nextStep === null; await db.userOnboarding.update({ where: { userId }, data: { completedSteps, currentStep: nextStep, ...(isCompleted && { completedAt: new Date() }) } }); // Track analytics track('onboarding_step_completed', { userId, stepId, stepTitle: step.title, points: step.points, totalCompleted: completedSteps.length, isActivated: isUserActivated(completedSteps) }); // Award points (gamification) if (step.points) { await awardPoints(userId, step.points, `Completed: ${step.title}`); } // Check activation const activated = isUserActivated(completedSteps); if (activated && !isUserActivated(onboarding.completedSteps)) { await handleUserActivation(userId); } return { completed: true, isActivated: activated, nextStep }; } function getNextStep(completedSteps: string[]): string | null { const remaining = onboardingSteps .filter(s => !completedSteps.includes(s.id)) .sort((a, b) => a.order - b.order); return remaining[0]?.id || null; } function isUserActivated(completedSteps: string[]): boolean { const requiredSteps = onboardingSteps.filter(s => s.required); const completedRequired = requiredSteps.filter(s => completedSteps.includes(s.id) ); return completedRequired.length >= ACTIVATION_THRESHOLD; } async function handleUserActivation(userId: string) { // Update user status await db.user.update({ where: { id: userId }, data: { isActivated: true, activatedAt: new Date() } }); // Track activation track('user_activated', { userId }); // Send activation email await sendActivationEmail(userId); // Notify sales (for high-value leads) await notifySalesIfQualified(userId); } ``` --- ## Onboarding UI Components ```tsx // components/onboarding/OnboardingChecklist.tsx 'use client'; import { useState, useEffect } from 'react'; import { CheckCircle, Circle, ChevronRight, X } from 'lucide-react'; import { onboardingSteps, OnboardingStep } from '@/lib/onboarding/config'; interface OnboardingProgress { completedSteps: string[]; currentStep: string | null; } export function OnboardingChecklist() { const [progress, setProgress] = useState(null); const [isExpanded, setIsExpanded] = useState(true); const [isDismissed, setIsDismissed] = useState(false); useEffect(() => { fetch('/api/onboarding/progress') .then(res => res.json()) .then(setProgress); }, []); if (!progress || isDismissed) return null; const completedCount = progress.completedSteps.length; const totalCount = onboardingSteps.length; const progressPercent = (completedCount / totalCount) * 100; // Hide if completed if (completedCount === totalCount) return null; return (
{/* Header */}
setIsExpanded(!isExpanded)} >
{completedCount}/{totalCount}

Getting Started

{completedCount === 0 ? "Let's set you up!" : `${totalCount - completedCount} steps left`}

{/* Steps */} {isExpanded && (
{onboardingSteps.map((step) => ( ))}
)}
); } function OnboardingStepItem({ step, isCompleted, isCurrent }: { step: OnboardingStep; isCompleted: boolean; isCurrent: boolean; }) { const handleClick = () => { if (isCompleted) return; if (step.action.type === 'link') { window.location.href = step.action.target; } else if (step.action.type === 'modal') { // Dispatch event to open modal window.dispatchEvent(new CustomEvent('open-modal', { detail: { id: step.action.target } })); } else if (step.action.type === 'trigger') { window.dispatchEvent(new CustomEvent(step.action.target)); } }; return ( ); } ``` --- ## Product Tour ```tsx // components/onboarding/ProductTour.tsx 'use client'; import { useState, useEffect, useCallback } from 'react'; import { createPortal } from 'react-dom'; interface TourStep { target: string; // CSS selector title: string; content: string; placement: 'top' | 'bottom' | 'left' | 'right'; action?: { label: string; onClick: () => void; }; } interface ProductTourProps { tourId: string; steps: TourStep[]; onComplete: () => void; onSkip?: () => void; } export function ProductTour({ tourId, steps, onComplete, onSkip }: ProductTourProps) { const [currentStep, setCurrentStep] = useState(0); const [targetRect, setTargetRect] = useState(null); const step = steps[currentStep]; // Find and highlight target element useEffect(() => { const target = document.querySelector(step.target); if (target) { setTargetRect(target.getBoundingClientRect()); // Scroll into view target.scrollIntoView({ behavior: 'smooth', block: 'center' }); // Add highlight class target.classList.add('tour-highlight'); return () => { target.classList.remove('tour-highlight'); }; } }, [step.target]); const handleNext = useCallback(() => { if (currentStep < steps.length - 1) { setCurrentStep(currentStep + 1); } else { onComplete(); } }, [currentStep, steps.length, onComplete]); const handlePrev = () => { if (currentStep > 0) { setCurrentStep(currentStep - 1); } }; const handleSkip = () => { onSkip?.(); }; if (!targetRect) return null; // Calculate tooltip position const tooltipStyle = calculateTooltipPosition(targetRect, step.placement); return createPortal( <> {/* Overlay with spotlight */}
{/* Tooltip */}
{/* Progress */}
{steps.map((_, i) => (
))}

{step.title}

{step.content}

{/* Actions */}
{currentStep > 0 && ( )}
, document.body ); } function calculateTooltipPosition( targetRect: DOMRect, placement: string ): React.CSSProperties { const offset = 16; switch (placement) { case 'bottom': return { top: targetRect.bottom + offset, left: targetRect.left + targetRect.width / 2, transform: 'translateX(-50%)' }; case 'top': return { bottom: window.innerHeight - targetRect.top + offset, left: targetRect.left + targetRect.width / 2, transform: 'translateX(-50%)' }; case 'right': return { top: targetRect.top + targetRect.height / 2, left: targetRect.right + offset, transform: 'translateY(-50%)' }; case 'left': return { top: targetRect.top + targetRect.height / 2, right: window.innerWidth - targetRect.left + offset, transform: 'translateY(-50%)' }; default: return {}; } } // CSS for highlighting /* .tour-highlight { position: relative; z-index: 51; box-shadow: 0 0 0 4px rgba(59, 130, 246, 0.5); border-radius: 8px; } */ ``` --- ## Activation Tracking ```typescript // lib/onboarding/activation.ts import { db } from '@/lib/db'; import { track } from '@/lib/analytics'; interface ActivationEvent { userId: string; event: string; metadata?: Record; } // Define activation criteria const ACTIVATION_EVENTS = [ { event: 'project_created', weight: 2 }, { event: 'team_member_invited', weight: 1 }, { event: 'integration_connected', weight: 2 }, { event: 'workflow_completed', weight: 3 }, { event: 'dashboard_customized', weight: 1 } ]; const ACTIVATION_THRESHOLD = 5; // Total weight needed export async function trackActivationEvent( event: ActivationEvent ): Promise<{ isActivated: boolean; progress: number }> { const { userId, event: eventName, metadata } = event; // Record the event await db.userEvent.create({ data: { userId, eventName, metadata: metadata || {}, occurredAt: new Date() } }); // Calculate activation progress const userEvents = await db.userEvent.findMany({ where: { userId }, select: { eventName: true } }); const uniqueEvents = new Set(userEvents.map(e => e.eventName)); let totalWeight = 0; ACTIVATION_EVENTS.forEach(ae => { if (uniqueEvents.has(ae.event)) { totalWeight += ae.weight; } }); const progress = Math.min(100, (totalWeight / ACTIVATION_THRESHOLD) * 100); const isActivated = totalWeight >= ACTIVATION_THRESHOLD; // Check if user just became activated const user = await db.user.findUnique({ where: { id: userId }, select: { isActivated: true } }); if (isActivated && !user?.isActivated) { await activateUser(userId); } return { isActivated, progress }; } async function activateUser(userId: string) { const activationTime = await calculateTimeToActivation(userId); await db.user.update({ where: { id: userId }, data: { isActivated: true, activatedAt: new Date() } }); track('user_activated', { userId, timeToActivation: activationTime }); // Trigger post-activation flow await triggerPostActivation(userId); } async function calculateTimeToActivation(userId: string): Promise { const user = await db.user.findUnique({ where: { id: userId }, select: { createdAt: true } }); if (!user) return 0; return Date.now() - user.createdAt.getTime(); } async function triggerPostActivation(userId: string) { // Send congratulations email await sendEmail(userId, 'activation-congrats'); // Schedule upgrade prompt await scheduleEmail(userId, 'upgrade-prompt', { delay: '3d' }); // Notify sales for qualified leads const user = await db.user.findUnique({ where: { id: userId }, include: { tenant: { include: { subscription: true } } } }); if (user?.tenant && isQualifiedLead(user.tenant)) { await notifySales(userId, 'activated_qualified_lead'); } } ``` --- ## Best Practices | Aspect | Recommendation | |--------|----------------| | **Time to Value** | < 5 minutes to first success | | **Steps** | Max 5-7 onboarding steps | | **Progress** | Always show completion % | | **Personalization** | Tailor based on use case | | **Help** | Offer chat/support at each step | | **Celebration** | Acknowledge completions | --- ## Fazit Effektives User Onboarding: 1. **Progressive Disclosure**: Nicht alles auf einmal zeigen 2. **Quick Wins**: Erste Erfolge ermöglichen 3. **Tracking**: Activation Events messen 4. **Iteration**: Kontinuierlich verbessern Gutes Onboarding ist der Schlüssel zu Retention. --- ## Bildprompts 1. "Onboarding checklist UI, progress bar and completed steps" 2. "Product tour spotlight on feature, tooltip explanation" 3. "User activation funnel, stages from signup to activated" --- ## Quellen - [Appcues Onboarding Guide](https://www.appcues.com/blog/user-onboarding) - [UserPilot Onboarding](https://userpilot.com/blog/user-onboarding/) - [Product-Led Growth Onboarding](https://www.productled.org/foundations/user-onboarding) - [Intercom Onboarding](https://www.intercom.com/blog/user-onboarding-best-practices/)