Onboarding Flow React block for Tailwind CSS v4
v1.0.2Updated in 1.9.0
A React onboarding flow for a marketplace app: welcome slides, notification and location asks, a short profile, preferences and a finish.
Swipe or drag the three welcome slides, use the dots, the arrow keys or Continue; Skip on the slides and Get started on the last one go to the permissions. From there a header shows a back button, a three part progress bar and Skip, which jumps straight to the finish with what you entered so far; every step change slides in from the side it belongs to, moves focus to the step title and is announced ("Step 2 of 4: Permissions"). Permissions: Allow opens the real browser prompt for notifications or location (with simulatePermissions it answers after 600 ms), Not now skips it; each card then shows Allowed, Denied with where to change it, Skipped with Allow anyway, or why the browser cannot do it. Continue is always enabled. A granted location fills the city with the nearest one. Profile: tap the photo to choose, crop or take a picture (optional), type the name, search the city (a full height sheet on phones), and tap interest chips, which are toggle buttons; the counter turns green at the minimum. Continue with a missing name or too few interests shows the errors and focuses the first. Preferences: pick Instantly, Daily or Weekly, toggle the newsletter card and read the summary line update. Finish celebrates with a bouncy check and one ring pulse (and a success haptic on phones), then Go to Home finishes and Start over starts over. Everything you enter is saved as you go, so a reload resumes on the same step with a short "Picking up where you left off" note. On laptops a picture beside the card cross fades per step and per slide; on phones the flow fills the screen with the main button pinned above the safe area. With reduced motion steps cross fade and the check simply appears.
Onboarding Flow is a Pro React block in the React blocks category, written in TypeScript for React 19 and Tailwind CSS v4. It installs as source with npx wingo-ui@latest add onboarding-flow, together with Alert, Avatar Upload, Button, Combobox, Image Cropper and 15 more and the npm packages motion, lucide-react, react-hook-form and zod.
Add to a project
Requirements: React 19 and Tailwind CSS v4, in Next.js (App Router or Pages), Vite or React Router. Installation guide · Next.js setup · Vite setup
npx wingo-ui@latest add onboarding-flowOnboarding Flow is part of Pro, with 298 more Pro items: $8 a month or $150 once. 14-day refund before you download any Pro item. See plans
Usage
Import OnboardingFlow from @/components/blocks/onboarding-flow.
// app/(app)/welcome/page.tsx
"use client";
import { useRouter } from "next/navigation";
import { OnboardingFlow } from "@/components/blocks/onboarding-flow";
export default function Page() {
const router = useRouter();
return (
<OnboardingFlow
user={{ name: "Emma Carter" }}
persistKey="onboarding:ana"
onUpload={async (blob) => {
const response = await fetch("/api/avatar", { method: "POST", body: blob });
if (!response.ok) throw new Error("Upload failed");
return (await response.json()).url;
}}
onComplete={async ({ profile, preferences, permissions }) => {
await fetch("/api/onboarding", { method: "POST", body: JSON.stringify({ profile, preferences, permissions }) });
router.push("/");
}}
/>
);
}Props
defaultStepdefault"welcome"OnboardingStepThe step shown first (uncontrolled, and the fallback before storage is read): "welcome" | "permissions" | "profile" | "preferences" | "done".
stepOnboardingStepThe current step, controlled (then nothing is restored from storage for it).
onStepChange(step: OnboardingStep) => voidRuns on every step change (mirror it to the URL).
slidesOnboardingSlide[]The welcome slides (onboarding-carousel's slide shape). Default: the three Acme Market slides from ONBOARDING_FLOW_SAMPLE with their vignettes and the side panel pictures; custom slides show their image in the side panel.
interestsOnboardingInterest[]The chips of the profile step: { id, label, icon, description }. Default: the 12 Acme Market categories of ONBOARDING_FLOW_SAMPLE.interests; icon takes a node or a sample icon name.
minInterestsdefault3numberHow many interests the profile needs; 0 makes them optional.
permissions("notifications" | "location")[]The permissions asked for, default both; [] skips the step.
simulatePermissionsdefaultfalsebooleanAnswer granted after 600 ms instead of calling the browser (demos, tests); a simulated location is Gheorgheni, Cluj-Napoca.
userOnboardingUserThe signed-up person ({ name, avatar, city }): the name the profile starts with and the side panel's profile card. Default ONBOARDING_FLOW_SAMPLE.user (Emma Carter).
defaultProfileOnboardingProfileThe profile the flow starts with ({ avatar, name, city, interests }); default empty with the name from user.
profileOnboardingProfileThe profile, controlled; onProfileChange gets every draft change.
onProfileChange(profile: OnboardingProfile) => voidEvery change of the profile draft (photo, name, city, interests).
defaultPreferencesOnboardingPreferencesThe preferences the flow starts with; default { frequency: "daily", newsletter: false }.
preferencesOnboardingPreferencesThe preferences, controlled; onPreferencesChange gets every change.
onPreferencesChange(preferences: OnboardingPreferences) => voidEvery change of the frequency or the newsletter switch.
persistKeydefault"onboarding-flow"stringLocal storage key that keeps the step, the drafts and the permission answers, so a closed app resumes where it stopped; empty keeps them in memory.
showSkipdefaulttruebooleanSkip on the slides and in the header of the later steps.
allowRestartdefaulttruebooleanThe Start over button on the finish screen, which clears the saved progress.
artdefault"image""image" | "none"The picture beside the card from 1024px, cross fading per step and per slide (tablets show the card alone, centered); none centers the card alone everywhere.
onComplete(result: OnboardingFlowResult) => (void | Promise<unknown>)Go to Home: gets { profile, preferences, permissions } (each permission granted, denied or skipped); a promise spins the button. Default: the toast "Welcome to Acme Market".
onUpload(blob: Blob, context: AvatarUploadContext) => Promise<string | void>Uploads the cropped photo and resolves its url. Default: simulated for 900 ms, then kept as a data url; a failed upload shows a note and the flow continues without a photo.
tonedefault"neutral"ToneThe primary buttons, the progress bar, the selected chips, the frequency thumb, the switch and the slides' button and dots.
radiusdefault"md""sm" | "md" | "lg"The card and the side panel, the permission tiles and the controls' corners.
loadingdefaultfalsebooleanThe card as skeletons of the profile step and a pulsing side panel.
animateIndefaulttruebooleanThe side panel and the card rise in once when the page mounts on the client (server html is already final).
labelsPartial<OnboardingFlowLabels>Every visible string: DEFAULT_ONBOARDING_FLOW_LABELS (English) by default; carousel, avatarUpload, cropper and validation carry the composed items' strings.
localedefault"en-US"stringPrices and distances on the slides and the page language (lang).
classNamesPartial<Record<OnboardingFlowSlot, string>>Classes for art, main, card, header, progress, step, title, description, footer, permission, interests and done.
Parts
DEFAULT_ONBOARDING_FLOW_LABELS
constantThe English strings for every visible label, the default of labels. Type: OnboardingFlowLabels.
ONBOARDING_FLOW_LABELS_EN
constantThe same English strings under their older name, kept for existing imports.
ONBOARDING_FLOW_SAMPLE
constantThe demo data: the user, a simulated position, the slides, interests, nearby listings, a sample posting and the art.
OnboardingFlowProps
typeProps of OnboardingFlow, including the div attributes of the root.
OnboardingFlowResult
typeWhat onComplete receives: { profile, preferences, permissions }.
OnboardingStep
type"welcome" | "permissions" | "profile" | "preferences" | "done", the values of step and defaultStep.
Examples
Permissions
Notifications and location with the reason for each; Allow answers after 600 ms here, Not now skips.
Profile, too few interests
Two interests picked out of the three needed: the counter, the error and the focus on the chips.
Done
The finish: a bouncy check with one ring pulse, three tips, Go to Home and Start over.
Brand tone
tone="brand" on the buttons, the progress, the chips, the frequency thumb and the switch.
British English
The default labels with locale en-GB, from the first slide.
No picture, no permissions
art="none" centers the card alone and permissions={[]} leaves the step out.
Dependencies
Files
- components/blocks/onboarding-flow.tsx
- components/blocks/onboarding-flow-data.ts
FAQ
Is the React Onboarding Flow block free?
The preview, props and docs are free; copying the source needs a Pro plan. See plans.
How do I install Onboarding Flow?
Run npx wingo-ui@latest add onboarding-flow. It also adds the Wingo UI items it builds on (Alert, Avatar Upload, Button, Combobox, Image Cropper and 15 more) and the npm packages motion, lucide-react, react-hook-form and zod. With shadcn, add the @wingo-ui registry with your API key, then run npx shadcn@latest add @wingo-ui/onboarding-flow.
How does Onboarding Flow behave on phones?
Profile: tap the photo to choose, crop or take a picture (optional), type the name, search the city (a full height sheet on phones), and tap interest chips, which are toggle buttons; the counter turns green at the minimum.
See also
More React blocks
Articles using this component
- Onboarding Checklist UI: The Get Started Card in React
Build it: screens and apps9 min read
- React Product Tour: Spotlight, Phone Dock, Hotspots
Component guides9 min read
- React Onboarding Flow: Slides, Permissions, Install
Build it: screens and apps9 min read
- React PWA Install Prompt for Chrome, Edge and iPhone
Mobile UI in React9 min read