User Onboarding
Build interactive onboarding tours with React Joyride
User Onboarding
Eden Stack includes a complete onboarding system using React Joyride with custom tooltips, Lucide icons, and confetti celebrations.
Features
- Server-side status check - Onboarding status loaded with auth, no client-side flash
- Custom tooltips - shadcn/ui Card with Framer Motion animations
- Icon support - Lucide icons in each step
- Confetti celebration - Fires when users complete the tour
- Persistent tracking - Completion stored in database per user
How It Works
Onboarding status is checked server-side in the _authenticated route, alongside subscription status:
// Route context includes onboarding data
const { user, subscription, onboarding } = Route.useRouteContext();
// Check if user needs onboarding
if (onboarding.needsOnboarding) {
// Show tour
}Creating Tour Steps
Define steps in apps/web/src/components/onboarding/{page}-tour-steps.ts:
import type { Step } from "react-joyride";
import { LayoutDashboard, Zap, Compass } from "lucide-react";
import type { OnboardingStepData } from "./onboarding-card";
export const myTourSteps: (Step & { data?: OnboardingStepData })[] = [
{
target: '[data-tour="welcome-card"]',
title: "Welcome",
content: "Your dashboard overview.",
placement: "bottom",
disableBeacon: true,
data: { icon: LayoutDashboard },
},
{
target: '[data-tour="quick-actions"]',
title: "Quick Actions",
content: "Create projects or start conversations.",
placement: "left",
data: { icon: Zap },
},
];Adding Tour Targets
Add data-tour attributes to elements you want to highlight:
<Card data-tour="welcome-card">
<CardHeader>Welcome!</CardHeader>
</Card>
<Button data-tour="quick-actions">New Project</Button>Using the Tour Component
import { DashboardTour } from "@/components/onboarding";
function DashboardPage() {
const { onboarding } = Route.useRouteContext();
return (
<div>
<DashboardTour needsOnboarding={onboarding.needsOnboarding} />
{/* Page content */}
</div>
);
}Database Schema
The users table includes an onboardingCompletedAt timestamp:
export const users = pgTable("users", {
// ...other fields
onboardingCompletedAt: timestamp("onboarding_completed_at"),
});API Endpoints
| Endpoint | Method | Description |
|---|---|---|
/api/onboarding/status | GET | Check if user completed onboarding |
/api/onboarding/complete | POST | Mark onboarding as complete |
Customization
Custom Icons
Pass any Lucide icon in the step's data property:
import { Rocket, Settings, Sparkles } from "lucide-react";
{
target: '[data-tour="upgrade"]',
title: "Upgrade",
content: "Unlock premium features.",
data: { icon: Sparkles },
}Multiple Tours
For feature-specific tours, add additional timestamp fields:
// In schema
dashboardTourCompletedAt: timestamp("dashboard_tour_completed_at"),
projectsTourCompletedAt: timestamp("projects_tour_completed_at"),Testing
Reset onboarding to test the tour again:
UPDATE users
SET onboarding_completed_at = NULL
WHERE email = 'your@email.com';Or use Drizzle Studio: bun run db:studio
Full documentation for Eden Stack users
This documentation is exclusively available to Eden Stack customers. Already purchased? Log in to access the full content.