# How to Add an User Progress Roadmap to Your App (Copy-Paste Prompts)

- Tool: App Features
- Last updated: July 2026

## TL;DR

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.

## 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** (ui): The 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.
- **Milestone node** (ui): A 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.
- **Step detail panel** (ui): Shown 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.
- **Progress state store** (data): A 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.
- **Completion trigger** (backend): A 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.
- **Animation layer** (ui): Celebrates 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.

## Data model

One table tracks step completions with a unique constraint to prevent duplicates and enforce step ordering. Run in the Supabase SQL editor:

```sql
create table public.user_progress (
  id uuid primary key default gen_random_uuid(),
  user_id uuid references auth.users(id) on delete cascade not null,
  roadmap_id text not null,
  step_key text not null,
  completed_at timestamptz not null default now(),
  metadata jsonb,
  constraint unique_user_roadmap_step unique (user_id, roadmap_id, step_key)
);

alter table public.user_progress enable row level security;

create policy "Users can view own progress"
  on public.user_progress for select
  using (auth.uid() = user_id);

create policy "Users can insert own progress"
  on public.user_progress for insert
  with check (auth.uid() = user_id);

-- Prevent users from updating completed_at or backdating completions
create policy "No direct updates to progress"
  on public.user_progress for update
  using (false);

create index user_progress_lookup_idx
  on public.user_progress (user_id, roadmap_id);
```

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 paths

### Lovable — fit 4/10, 2–4 hours

Lovable generates a clean timeline UI with Supabase persistence and Framer Motion transitions in one prompt — best overall AI-tool path for web onboarding flows and product activation roadmaps.

1. Create a Lovable project with Lovable Cloud enabled so Supabase auth and the user_progress table are provisioned
2. Paste the prompt below and let Agent Mode generate the roadmap component, Supabase fetch on mount, completion handler, and celebration animation
3. Test on the published URL: sign in as two different users and verify each sees only their own progress
4. Follow up with 'Add prerequisite validation to the complete-step Edge Function — step N cannot be completed unless step N-1 is already in user_progress' if step ordering is required

Starter prompt:

```
Build an interactive onboarding roadmap component. Steps are defined as a static array: [{key: 'profile', label: 'Complete your profile', description: 'Add your name, photo, and bio.'}, {key: 'invite', label: 'Invite a teammate', description: 'Bring your first collaborator on board.'}, {key: 'project', label: 'Create your first project', description: 'Set up a workspace to track your work.'}, {key: 'publish', label: 'Publish your first update', description: 'Share progress with your team.'}]. On mount, fetch completed step_keys from Supabase table user_progress WHERE user_id = auth.uid() AND roadmap_id = 'onboarding'. Render all steps vertically with a connector line. Completed steps: filled green circle with checkmark. Current step: accent-coloured outlined circle, bold label, expanded description visible. Upcoming steps: grey dashed circle, dimmed label. Clicking any step opens a shadcn/ui Sheet showing title, description, completion date (if done), and a 'Mark complete' button (only for current step). On mark complete: insert row into user_progress (user_id, roadmap_id: 'onboarding', step_key, completed_at: now()); validate that the previous step_key is already complete before inserting. Use canvas-confetti to fire a burst from the completed node. Animate the node colour change with Framer Motion. If all steps are complete, show a full-screen celebration state with a 'You're all set!' message.
```

Limitations:

- Complex dependency logic (step B requires both A and C to be complete) needs a manual Edge Function — Lovable's first generation usually only enforces linear ordering
- Confetti animation sometimes missing from first generation; prompt 'add canvas-confetti burst on step completion — fire once, not on every render' as a follow-up
- Lovable does not generate a Supabase trigger for prerequisite validation by default; the check runs client-side first and should be backed by server validation for production

### Flutterflow — fit 5/10, 2–4 hours

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.

1. In 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
2. Create App State variables: completedSteps (list of strings), currentStep (string), and isLoading (bool)
3. On 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
4. Wire conditional visibility on each node widget: use FlutterFlow Condition 'list completedSteps contains step_key' to switch between completed, current, and upcoming status icons
5. Add 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

Limitations:

- 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

### V0 — fit 3/10, 3–5 hours

V0 generates polished step-indicator components with Tailwind and shadcn/ui — excellent for web onboarding flows and SaaS product activation pages embedded in a Next.js app.

1. Prompt V0 to generate the roadmap component with status-driven node rendering and a Sheet panel for step detail
2. Add Supabase env vars in the Vars panel; wire the mount fetch via a Server Component and the completion action via a Server Action
3. Run the SQL schema in the Supabase SQL editor to create the user_progress table with RLS policies
4. Deploy and test signing in as multiple users to confirm RLS isolates each user's progress data

Starter prompt:

```
Create a Next.js onboarding roadmap component using Tailwind CSS and shadcn/ui. Steps array (static): [{key:'profile', label:'Set up your profile'}, {key:'team', label:'Add your team'}, {key:'billing', label:'Choose a plan'}, {key:'launch', label:'Go live'}]. Server Component fetches completed step_keys for the current user from Supabase table user_progress (WHERE user_id = auth.uid(), roadmap_id = 'onboarding'). Render a vertical stepper with connector lines between nodes. Node states: completed (filled green circle, CheckIcon), current (accent ring, pulsing dot), upcoming (grey dashed circle). Clicking any node opens a shadcn/ui Sheet with step title, description, completion date (completed steps), and a 'Complete this step' button (current step only). 'Complete this step' calls a Server Action that validates the previous step is complete in Supabase, then inserts into user_progress; returns the updated progress list. On new completion, use framer-motion to animate the node fill transition and fire canvas-confetti. Show a completion banner when all 4 steps are done. Handle loading state during Supabase fetch with skeleton nodes.
```

Limitations:

- V0 output requires manual Supabase integration — V0 does not provision the database like Lovable Cloud does
- Mobile-only platforms will not use V0; FlutterFlow is the right choice for native mobile progress trackers
- V0 may generate localStorage for step caching — replace with Server Component Supabase fetch to avoid SSR hydration errors

### Custom — fit 4/10, 2–4 days

Custom development adds branching roadmaps, multi-user collaborative progress boards, external event triggers, and dependency graphs with non-linear step ordering.

1. Design the dependency graph schema: a step_dependencies table mapping step_key to required_step_key, allowing arbitrary prerequisites beyond linear ordering
2. Build a Supabase RPC function validate_and_complete_step(p_user_id, p_roadmap_id, p_step_key) that checks all required predecessors in step_dependencies before inserting into user_progress
3. Supabase Realtime subscription on user_progress enables live multi-user boards where team members see each other's progress update instantly
4. Lottie animation files per milestone (not generic confetti) for brand-specific celebration moments — source from Lottiefiles.com or commission a motion designer

Limitations:

- Custom dependency graphs are complex to debug and test — invest in a visual admin panel that shows the dependency tree before shipping to users
- Multi-user collaborative progress requires careful RLS design: users need SELECT on teammates' progress without being able to INSERT as them

## Gotchas

- **Progress resets on page refresh because it's stored in local state only** — 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** — 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** — 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** — 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** — 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

## 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.

---

Source: https://www.rapidevelopers.com/app-features/interactive-user-roadmap-or-progress-bar
© RapidDev — https://www.rapidevelopers.com/app-features/interactive-user-roadmap-or-progress-bar
