Feature spec
BeginnerCategory
maps-location
Build with AI
2–4 hours with Lovable or FlutterFlow
Custom build
2–4 days custom dev
Running cost
$0/mo up to 10K users
Works on
Everything it takes to ship an Interactive User Roadmap or Progress Bar — parts, prompts, and real costs.
An interactive roadmap or progress bar needs a visual track component, per-step state stored in Supabase (not just React state), a completion trigger that validates step ordering, and an animated celebration on milestone completion. With Lovable or FlutterFlow you can ship a working progress tracker in 2–4 hours for $0/month up to 10,000 users — the only cost at scale is Supabase Pro at $25/month. Progress resets on page refresh are the most common first-build failure.
What an Interactive Roadmap or Progress Bar Actually Requires
A progress roadmap shows users where they are in a journey — onboarding, a course, a certification path, or a product activation sequence. The visual layer (connected nodes, colour-coded status, animated transitions) is the easy part. The product decisions that determine whether it succeeds are: does progress persist across sessions? Can users skip ahead? Does tapping a step show detail without navigating away? Is there a completion celebration that fires only once? The data model is a user_progress table in Supabase with a unique constraint on (user_id, roadmap_id, step_key) — this is what prevents duplicates and enforces step ordering. Most AI-generated first builds store progress in React useState or Flutter App State and lose everything on page refresh.
What users consider table stakes in 2026
- All steps are visible at once — users can see the entire journey ahead, not just the current step
- The current step is visually distinct: different colour, bold label, and a pulsing or highlighted border
- Completed steps show a checkmark or filled indicator; future steps are dimmed but not hidden
- Tapping a completed step expands its detail without navigating away from the roadmap view
- Progress persists across sessions and devices — closing and reopening the app never resets the roadmap
- An animated transition (colour fill, confetti, or Lottie animation) fires once when a new step is completed
- The roadmap is accessible: screen readers announce current step number and total count
Anatomy of the Interactive Roadmap Feature
Six components — the persistence layer and completion trigger are where first builds break. The visual track and animation components AI tools generate reliably; step ordering validation requires explicit prompting.
Progress track
UIThe visual timeline connecting milestone nodes — horizontal or vertical depending on content density. In Flutter: the timelines_plus package (pub.dev) provides a highly configurable TimelineTile with connector and indicator customisation. In React/Lovable: a custom Tailwind CSS flex or grid layout with step nodes and connector lines; Framer Motion (framer-motion npm) animates the fill transition when a step completes.
Note: Vertical timelines work better on mobile where horizontal space is limited. Horizontal step indicators work for 3–5 step onboarding flows but become cramped beyond 6 steps. Use vertical layout if the roadmap has more than 5 steps or if each step has a label longer than two words.
Milestone node
UIA circle or icon at each step position indicating status: completed (filled, checkmark icon, green), current (outlined, accent colour, optionally pulsing), upcoming (dimmed grey, locked padlock icon). In Flutter: AnimatedContainer with BoxDecoration transitions between status colours. In React: shadcn/ui Badge with variant prop switching between 'default', 'secondary', and 'outline' based on status.
Note: The visual distinction between completed and current must be obvious without relying on colour alone — use shape or icon differences for accessibility. A simple checkmark vs circle vs dashed-circle set reads well across all users.
Step detail panel
UIShown when the user taps a step node — displays the step title, description, what action is required, the completion date for done steps, and an action button for the current step. In Flutter: showModalBottomSheet() with a DraggableScrollableSheet so users can expand for more detail. In React: shadcn/ui Sheet component sliding in from the right, or an Accordion that expands inline without leaving the roadmap.
Note: Never navigate away from the roadmap to show step detail. Navigation breaks the spatial metaphor — the user loses their sense of position in the journey. Show all detail in a modal, bottom sheet, or in-place expansion.
Progress state store
DataA Supabase table user_progress with columns user_id, roadmap_id, step_key, completed_at, and optional metadata jsonb. Fetched on component mount to restore progress across sessions. Updated immediately on step completion via a Server Action (Next.js) or Supabase insert directly from the client. The unique constraint on (user_id, roadmap_id, step_key) prevents duplicate completion records.
Note: This is the most commonly skipped component in AI-generated builds. Always fetch from Supabase on mount, never rely on local state as the source of truth for progress. A Supabase Realtime subscription on the user_progress table lets multi-device users see progress update in the open tab when they complete a step on another device.
Completion trigger
BackendA Server Action, Supabase Edge Function, or Supabase RPC that handles the 'mark step complete' action. Validates that prerequisite steps are already in user_progress before inserting the new row. Updates the completed_at timestamp. Optionally calls a push notification or gamification reward function. Returns the updated progress state to the client.
Note: Client-side prerequisite checking is not secure — a motivated user can call the insert API directly. Put prerequisite validation in the Edge Function or a PostgreSQL trigger that checks the user_progress table before accepting the new row.
Animation layer
UICelebrates step completion so the action feels rewarding. Two options: canvas-confetti (npm) — a 200-line library that fires a confetti burst from the completed node position, free and zero dependencies. Lottie animations (lottie-flutter / lottie-react) — plays a pre-designed animation file (Lottiefiles marketplace has hundreds of celebration options). Store a 'celebrated_steps' set in Supabase or localStorage to prevent the animation replaying on every component remount.
Note: canvas-confetti is the fastest to implement (one function call) and works in both web and React Native Web. Lottie provides more design control and is better for branded milestone celebrations. Both fire client-side — no API cost.
The data model
One table tracks step completions with a unique constraint to prevent duplicates and enforce step ordering. Run in the Supabase SQL editor:
1create table public.user_progress (2 id uuid primary key default gen_random_uuid(),3 user_id uuid references auth.users(id) on delete cascade not null,4 roadmap_id text not null,5 step_key text not null,6 completed_at timestamptz not null default now(),7 metadata jsonb,8 constraint unique_user_roadmap_step unique (user_id, roadmap_id, step_key)9);1011alter table public.user_progress enable row level security;1213create policy "Users can view own progress"14 on public.user_progress for select15 using (auth.uid() = user_id);1617create policy "Users can insert own progress"18 on public.user_progress for insert19 with check (auth.uid() = user_id);2021-- Prevent users from updating completed_at or backdating completions22create policy "No direct updates to progress"23 on public.user_progress for update24 using (false);2526create index user_progress_lookup_idx27 on public.user_progress (user_id, roadmap_id);Heads up: The unique constraint on (user_id, roadmap_id, step_key) is the database-level enforcement of non-duplication — the application cannot insert the same step twice, even if the user calls the API multiple times. The UPDATE policy set to false means progress can only be added, never backdated, keeping the audit trail clean. The roadmap_id column lets you have multiple different roadmaps (onboarding, certification, feature activation) all tracked in the same table.
Build it — pick your path
Each tool fits this feature differently. Switch tabs to compare fit, time, the exact steps, and a copy-paste prompt.
The best mobile path — timelines_plus widget integrates in the FlutterFlow widget tree, App State tracks the current step, and Supabase completion actions wire visually in the Action editor.
Step by step
- 1In the FlutterFlow widget tree, add a ListView and inside each item use a custom widget or a Row with Icon + connector line to build the timeline; or add the timelines_plus package via the pubspec.yaml custom dependency and use it in a Custom Widget
- 2Create App State variables: completedSteps (list of strings), currentStep (string), and isLoading (bool)
- 3On page load, add a Supabase Query action to fetch user_progress WHERE user_id = currentUser.uid AND roadmap_id = 'onboarding'; update completedSteps App State with the result
- 4Wire conditional visibility on each node widget: use FlutterFlow Condition 'list completedSteps contains step_key' to switch between completed, current, and upcoming status icons
- 5Add a 'Mark Complete' button with an action flow: (1) Supabase Insert into user_progress, (2) update completedSteps App State, (3) trigger a Lottie animation widget to play the celebration
Where this path bites
- Highly custom timeline designs (curved connectors, animated fill) require a Custom Widget code block with custom Paint code; FlutterFlow's built-in widgets do not support this level of customisation
- FlutterFlow's Condition panel uses 'list contains item' logic correctly, but ensure you are comparing string step_key values — comparing widget index to list length is a common gotcha that shows all steps as 'upcoming'
- Prerequisite step validation should be backed by a Supabase Edge Function; the FlutterFlow action flow alone cannot reliably enforce ordering if the API is called directly
Third-party services you'll need
This feature has almost no third-party service cost — Supabase handles persistence, and the animation libraries are open-source.
| Service | What it does | Free tier | Paid from |
|---|---|---|---|
| Supabase | Progress storage with RLS, real-time subscription for multi-device sync | Free tier: 500MB DB, 50,000 MAU auth | Pro $25/mo |
| canvas-confetti (npm) | Lightweight confetti burst on step completion — no dependencies, 7KB | Open-source, free | No cost |
| lottie-flutter / lottie-react | Animated milestone icons and celebration sequences from Lottiefiles | Open-source, free | No cost (Lottie animation files from Lottiefiles.com: free community files; premium from $29/mo) |
Swipe the table sideways to see pricing.
What it costs to run
Drag through the tiers to see how your monthly bill scales with users — no surprises later.
Estimated monthly running cost
Supabase free tier handles progress rows easily at this scale. No third-party service costs. canvas-confetti runs on the client.
Estimates use verified 2026 pricing and assume typical usage per user. Your real bill depends on activity, storage, and third-party plans.
What breaks when AI tools build this
The failures people actually hit on their first build — the symptom, why it happens, and the exact fix.
Progress resets on page refresh because it's stored in local state only
Symptom: AI-generated roadmaps frequently store the current step in React useState or Flutter App State. When the user refreshes the browser or reopens the app, the state initialises from zero and all progress is lost. Users who complete step 2 of onboarding, close the browser, and return the next day see step 1 as current again — a trust-breaking experience for any onboarding flow.
Fix: Fetch the completed step_keys from Supabase on component mount — read from the database, never from memory. Set the local state only after the database query returns. The pattern is: show skeleton nodes → fetch user_progress → derive current step from the fetched list → render. Never initialise state from a hardcoded 'step 0' default.
Users can skip steps by calling the completion API directly
Symptom: AI-generated completion handlers often only check on the client side: 'if this is the current step, mark it complete.' A user who calls the Supabase insert endpoint directly (or modifies the JavaScript) can insert any step_key into user_progress regardless of ordering. In a certification or onboarding flow with business logic tied to step completion, this breaks the intended sequence.
Fix: Add prerequisite validation in the Edge Function or Server Action. Before inserting into user_progress, query the table for the required prior step. If it's absent, return an error. Implement this as a database trigger or RPC function so the check runs at the database level, not just in application code.
RLS missing lets users update other users' progress rows
Symptom: Without Row Level Security, any authenticated user can INSERT into user_progress with any user_id they choose — fabricating completion of steps on behalf of another user. In gamified products or certifications where progress has real value (access to features, credentials), this is an integrity breach.
Fix: Enable RLS on user_progress. Add INSERT WITH CHECK (auth.uid() = user_id) and SELECT USING (auth.uid() = user_id). Block UPDATE entirely with USING (false) — completions are append-only, never modifiable. Test with two separate user sessions to confirm isolation.
Confetti and Lottie animations play on every component remount
Symptom: canvas-confetti fires when the 'just completed' condition is true. If that condition is derived from 'the step is in completedSteps' rather than 'the step was just now added to completedSteps', the animation plays every time the component mounts: on page load, on navigation return, on tab refocus — anywhere the component re-renders.
Fix: Track a 'celebrated_steps' set stored in localStorage or Supabase. On mount, load the set. When a step completes, check whether it is already in celebrated_steps before firing the animation. After firing, add the step_key to celebrated_steps. This ensures the animation plays exactly once — at the moment of first completion.
FlutterFlow timeline shows all steps as upcoming after Supabase fetch
Symptom: FlutterFlow's conditional visibility panel uses a 'list contains item' check. A common mistake is configuring the condition to compare the step's index (integer) against the completedSteps list (list of strings). The types don't match, the check always returns false, and every step node renders with the 'upcoming' style regardless of actual completion state.
Fix: In FlutterFlow's Condition editor, set the condition as: list 'completedSteps' (type: list of strings) contains value 'step_key' (type: string). Ensure the Supabase query action populates completedSteps as a list of strings (the step_key column values), not as a list of full objects. Use a debug print action in the flow to log the completedSteps contents before the condition check if the result still looks wrong.
Best practices
Always read progress from Supabase on mount and write to Supabase on completion — never use in-memory state as the source of truth for progress
Show all steps simultaneously from day one — users who can see the full journey ahead are more motivated to continue than those who see only the next step
Enforce step ordering server-side in the Edge Function, not just in the UI — client-side validation is easily bypassed
Store a celebrated_steps set to ensure animations fire once on completion, not on every render cycle
Add relative timestamps to completed steps ('Completed 2 days ago') — users returning to the roadmap after a break benefit from the context
Make step descriptions scannable in 5 seconds — a 200-word step description inside a bottom sheet loses users who want to know 'is this done or not' at a glance
Use accessible colour choices for step status — never rely on red/green alone to distinguish completed vs upcoming; pair colour with icon shape for colour-blind users
When You Need Custom Development for the Roadmap Feature
Lovable and FlutterFlow handle linear onboarding flows well. Custom work is needed when:
- Progress depends on external events rather than user clicks: a payment confirmed via Stripe webhook, a document signed via DocuSign callback, or a third-party API polling result — these require webhook receivers and async state updates beyond what AI tools generate
- Multi-user collaborative roadmap: a team shares one progress board where any team member can complete steps, and all members see updates in real time — requires Supabase Realtime + RLS that scopes to team membership, not individual users
- Branching paths where different user roles or subscription tiers follow different roadmaps with different step sequences — non-linear dependency graphs require a step_dependencies table and more complex prerequisite logic
- Gamification rewards tightly coupled to milestones: XP points, badges, or feature unlocks that trigger on step completion require a rewards service that the completion Edge Function must call atomically
RapidDev builds these features for production
Real auth, database, and payments — fixed price, delivered in weeks. You own 100% of the code.
Frequently asked questions
How do I save user progress across sessions in Supabase?
Create a user_progress table with columns user_id, roadmap_id, step_key, and completed_at. Enable RLS with a policy that restricts read and write to the authenticated user. On component mount, fetch the completed step_keys from Supabase for the current user and roadmap. Derive the current step as the first step_key not yet in the fetched list. Write to this table immediately on each step completion — never rely on React state or Flutter App State to persist progress across browser sessions.
Can I build a step-by-step onboarding roadmap in FlutterFlow?
Yes. Use a ListView with custom step node widgets (a Row with an Icon for status, a connector line, and a Text label). Store completed steps as a list of strings in App State, populated from a Supabase query on page load. Wire conditional visibility on each node to switch between completed, current, and upcoming icons using FlutterFlow's 'list contains' condition. Add a Supabase Insert action on the 'Mark complete' button to write to user_progress.
How do I prevent users from skipping steps in a progress tracker?
Add prerequisite validation in a Supabase Edge Function or Server Action. Before inserting into user_progress, query the table for the step that must precede the requested step. If the prerequisite is absent, return a 400 error. Do not rely on client-side checks alone — a determined user can call the insert endpoint directly. For extra protection, add a PostgreSQL function as a trigger on the user_progress INSERT that validates prerequisites at the database level.
What libraries make a good animated progress timeline in Flutter?
timelines_plus (pub.dev) is the most configurable library for timeline UIs in Flutter — it handles node indicators, connector lines, and spacing with a clean API. For completion animations, lottie-flutter plays Lottie JSON animation files (source from Lottiefiles.com) for polished milestone celebrations. For simple confetti, the confetti (pub.dev) package is lightweight and easy to trigger. All three are open-source and free.
Can I show different roadmaps to different user roles?
Yes — use the roadmap_id column in user_progress. Store the roadmap configuration (step keys, labels, descriptions) as a JSON object in your app code or a Supabase roadmaps table, keyed by roadmap_id. Fetch the appropriate roadmap based on the user's role from Supabase Auth metadata or a user_profiles table. Each user sees only the steps that belong to their roadmap_id, and the RLS policy scopes their progress rows to that same roadmap.
How do I add confetti when a user completes a milestone?
Import canvas-confetti (npm) in your React component. After a successful Supabase insert, call confetti({ particleCount: 100, spread: 70, origin: { y: 0.6 } }). To prevent replay on every remount, store completed step keys in localStorage under a key like 'celebrated_steps'. On mount, read the set. After firing, add the step_key to the set. Only fire confetti if the step_key is not already in the set. This ensures each milestone celebrates exactly once.
Is a roadmap the same as an onboarding flow?
Similar but distinct. An onboarding flow typically uses fullscreen modals or guided overlays that block the rest of the app until the user completes each step. A roadmap shows the full journey as a persistent UI element — users can see where they are, where they've been, and what's ahead at any time without being forced through a sequence. Roadmaps work best after the initial onboarding flow has introduced the app's core concept.
How many steps should a user roadmap have?
3–7 steps is the effective range. Below 3 steps the feature does not justify the visual complexity of a timeline. Above 7 steps users feel overwhelmed and completion rates drop sharply. If your journey has more than 7 stages, consider grouping them into phases (Phase 1: Setup, Phase 2: Growth, Phase 3: Scale) with 3–4 steps each, and show one phase at a time while indicating overall phase progress.
Need this feature production-ready?
RapidDev builds an interactive user roadmap or progress bar into real apps — auth, database, payments — at $13K–$25K.
Book a free consultation30-min call. No commitment.