Visão Geral
Onboarding é uma das etapas mais críticas de qualquer SaaS — a maioria dos churns começa aqui:
✅ Alto impacto em ativação e retenção de longo prazo
✅ Usuários são mais tolerantes a variações durante o setup
✅ Conversão fácil de medir (completou step X, chegou ao aha moment)
Neste guia, você aprenderá a testar:
Número de steps no wizard
Ordem das etapas
Tela de boas-vindas
Exemplo 1: Wizard Curto vs Longo
Testar se um onboarding mais curto aumenta a taxa de conclusão.
Criar o experimento no dashboard
Acesse o dashboard do Testly
Clique em “Novo Experimento”
Nome: onboarding-wizard-length
Variantes:
Controle : Wizard completo (5 steps)
Variante B : Wizard curto (3 steps essenciais)
Implementação
components/OnboardingWizard.tsx
'use client' ;
import { useState } from 'react' ;
import { useExperiment } from '@testlyjs/react' ;
const fullSteps = [
{ id: 'profile' , title: 'Seu Perfil' , component: ProfileStep },
{ id: 'team' , title: 'Seu Time' , component: TeamStep },
{ id: 'first-experiment' , title: 'Primeiro Experimento' , component: FirstExperimentStep },
{ id: 'sdk-install' , title: 'Instalar SDK' , component: SDKInstallStep },
{ id: 'complete' , title: 'Concluído' , component: CompleteStep },
];
const shortSteps = [
{ id: 'first-experiment' , title: 'Primeiro Experimento' , component: FirstExperimentStep },
{ id: 'sdk-install' , title: 'Instalar SDK' , component: SDKInstallStep },
{ id: 'complete' , title: 'Concluído' , component: CompleteStep },
];
export function OnboardingWizard () {
const { variant , loading , convert } = useExperiment ( 'onboarding-wizard-length' );
const [ currentStep , setCurrentStep ] = useState ( 0 );
if ( loading ) return < WizardSkeleton /> ;
const steps = variant === 'variant-b' ? shortSteps : fullSteps ;
const CurrentStepComponent = steps [ currentStep ]. component ;
const handleNext = () => {
convert ( `step_completed_ ${ steps [ currentStep ]. id } ` );
if ( currentStep === steps . length - 1 ) {
convert ( 'onboarding_completed' );
window . location . href = '/dashboard' ;
} else {
setCurrentStep (( prev ) => prev + 1 );
}
};
const handleSkip = () => {
convert ( 'onboarding_skipped' );
window . location . href = '/dashboard' ;
};
return (
< div className = "min-h-screen bg-gray-50 flex items-center justify-center p-4" >
< div className = "bg-white rounded-2xl shadow-lg w-full max-w-xl p-8" >
{ /* Progress */ }
< div className = "flex items-center gap-2 mb-8" >
{ steps . map (( step , index ) => (
< div
key = { step . id }
className = { `h-1.5 flex-1 rounded-full transition-colors ${
index <= currentStep ? 'bg-blue-600' : 'bg-gray-200'
} ` }
/>
)) }
</ div >
{ /* Step label */ }
< p className = "text-sm text-gray-500 mb-2" >
Passo { currentStep + 1 } de { steps . length }
</ p >
< h2 className = "text-2xl font-bold text-gray-900 mb-6" >
{ steps [ currentStep ]. title }
</ h2 >
{ /* Content */ }
< CurrentStepComponent />
{ /* Actions */ }
< div className = "flex items-center justify-between mt-8" >
< button
onClick = { handleSkip }
className = "text-sm text-gray-400 hover:text-gray-600"
>
Pular por agora
</ button >
< button
onClick = { handleNext }
className = "bg-blue-600 hover:bg-blue-700 text-white px-6 py-2.5 rounded-lg font-semibold"
>
{ currentStep === steps . length - 1 ? 'Ir para o Dashboard' : 'Continuar' }
</ button >
</ div >
</ div >
</ div >
);
}
Rastreie cada step individual com convert('step_completed_X'). Isso permite identificar onde os usuários abandonam o onboarding, independentemente do resultado do experimento.
Exemplo 2: Tela de Boas-Vindas
Testar mensagem de boas-vindas personalizada vs genérica.
components/WelcomeScreen.tsx
'use client' ;
import { useExperiment } from '@testlyjs/react' ;
interface WelcomeScreenProps {
userName : string ;
onContinue : () => void ;
}
export function WelcomeScreen ({ userName , onContinue } : WelcomeScreenProps ) {
const { variant , convert } = useExperiment ( 'onboarding-welcome-message' );
const handleContinue = () => {
convert ( 'welcome_screen_continued' );
onContinue ();
};
return (
< div className = "text-center py-8" >
{ variant === 'variant-b' ? (
// Personalizado com nome
<>
< div className = "text-5xl mb-4" > 👋 </ div >
< h1 className = "text-3xl font-bold text-gray-900 mb-3" >
Olá, { userName } !
</ h1 >
< p className = "text-gray-600 mb-8 max-w-sm mx-auto" >
Você está a 3 passos de rodar seu primeiro experimento. Vamos lá?
</ p >
</>
) : (
// Genérico
<>
< div className = "text-5xl mb-4" > 🚀 </ div >
< h1 className = "text-3xl font-bold text-gray-900 mb-3" >
Bem-vindo ao Testly!
</ h1 >
< p className = "text-gray-600 mb-8 max-w-sm mx-auto" >
Configure sua conta em poucos passos e comece a testar.
</ p >
</>
) }
< button
onClick = { handleContinue }
className = "bg-blue-600 hover:bg-blue-700 text-white px-8 py-3 rounded-lg font-semibold"
>
Começar Setup
</ button >
</ div >
);
}
Exemplo 3: Checklist vs Wizard
Testar se um checklist livre (vs wizard guiado) aumenta a taxa de ativação.
components/OnboardingChecklist.tsx
'use client' ;
import { useState } from 'react' ;
import { useExperiment } from '@testlyjs/react' ;
const checklistItems = [
{ id: 'create-experiment' , label: 'Criar seu primeiro experimento' , href: '/experiments/new' },
{ id: 'install-sdk' , label: 'Instalar o SDK' , href: '/docs/sdk/installation' },
{ id: 'add-variant' , label: 'Configurar variantes' , href: null },
{ id: 'go-live' , label: 'Ativar experimento' , href: null },
];
export function OnboardingChecklist () {
const { variant , convert } = useExperiment ( 'onboarding-format' );
const [ completed , setCompleted ] = useState < Set < string >>( new Set ());
if ( variant !== 'variant-b' ) {
// Variante B recebe checklist; controle recebe wizard (outro componente)
return null ;
}
const handleItemClick = ( itemId : string , href : string | null ) => {
const newCompleted = new Set ( completed ). add ( itemId );
setCompleted ( newCompleted );
convert ( `checklist_item_ ${ itemId } ` );
if ( newCompleted . size === checklistItems . length ) {
convert ( 'onboarding_completed' );
}
if ( href ) window . location . href = href ;
};
return (
< div className = "bg-white rounded-xl border border-gray-200 p-6" >
< h2 className = "text-xl font-bold text-gray-900 mb-1" > Primeiros Passos </ h2 >
< p className = "text-sm text-gray-500 mb-6" >
{ completed . size } / { checklistItems . length } concluídos
</ p >
< div className = "space-y-3" >
{ checklistItems . map (( item ) => {
const isDone = completed . has ( item . id );
return (
< button
key = { item . id }
onClick = { () => handleItemClick ( item . id , item . href ) }
className = "w-full flex items-center gap-3 p-3 rounded-lg hover:bg-gray-50 text-left transition-colors"
>
< div
className = { `w-5 h-5 rounded-full border-2 flex items-center justify-center flex-shrink-0 ${
isDone
? 'bg-green-500 border-green-500'
: 'border-gray-300'
} ` }
>
{ isDone && (
< svg className = "w-3 h-3 text-white" fill = "none" viewBox = "0 0 24 24" stroke = "currentColor" >
< path strokeLinecap = "round" strokeLinejoin = "round" strokeWidth = { 3 } d = "M5 13l4 4L19 7" />
</ svg >
) }
</ div >
< span className = { `text-sm font-medium ${ isDone ? 'line-through text-gray-400' : 'text-gray-900' } ` } >
{ item . label }
</ span >
{ ! isDone && item . href && (
< svg className = "w-4 h-4 text-gray-400 ml-auto" fill = "none" viewBox = "0 0 24 24" stroke = "currentColor" >
< path strokeLinecap = "round" strokeLinejoin = "round" strokeWidth = { 2 } d = "M9 5l7 7-7 7" />
</ svg >
) }
</ button >
);
}) }
</ div >
</ div >
);
}
Hipótese : Checklist livre dá sensação de controle e pode aumentar engajamento vs wizard linear que força uma ordem.
Métricas de Onboarding
Eventos importantes para rastrear:
step_completed_profile → usuário completou o perfil
step_completed_sdk-install → usuário instalou o SDK
step_completed_first-experiment → criou primeiro experimento
onboarding_completed → concluiu todo o fluxo
onboarding_skipped → pulou o onboarding
Métricas derivadas (calcule externamente):
Taxa de conclusão = onboarding_completed / onboarding_started
Drop-off por step = onde a taxa de step_completed cai
Time to ativação = tempo entre signup e primeiro evento real
Próximos Passos
Hero Section Teste títulos e layouts de hero
Pricing Page Teste estruturas de preço
Call-to-Action Otimize botões e formulários
Boas Práticas Metodologias de experimentação