# How to Add Goal Setting and Tracking to Your App — Copy-Paste Prompts Inside

- Tool: App Features
- Last updated: July 2026

## TL;DR

A goal-setting feature needs four things: a SMART goal creation form with a numeric target and deadline, a progress ring that shows percentage completion, a milestone list for sub-goals, and a weekly check-in scheduler. With Lovable or V0 you can ship the core in 4–7 hours. Running cost is $0/month on the free tier — Supabase Pro ($25/mo) is the first cost you hit at around 1K users.

## What a Goal-Setting and Tracking Feature Actually Is

A goal-setting and tracking feature lets users define a measurable target — 'Save $5,000 by December', 'Read 24 books this year', 'Run a 5K under 30 minutes' — and log incremental progress toward it. The product decisions that matter most are: whether goals are numeric (target + current value) or binary (done / not done); whether milestones break the goal into stages; how progress is visualized (ring, bar, sparkline); and whether the app proactively nudges users to log updates via a check-in cadence. AI tools generate the CRUD form and Recharts radial chart reliably in the first prompt. The projected completion date calculation, the milestone parent-child relationship, and the weekly check-in email scheduler are where builds need follow-up prompting.

## Anatomy of the Feature

Eight components. The form, chart, and log table are what AI tools produce confidently. The milestone parent-child logic, projected completion date RPC, and check-in scheduler are the three pieces that need explicit prompting or a second pass.

- **Goal Creation Form** (ui): A multi-step form built with react-hook-form and zod on web, or a Flutter Stepper widget. Fields: goal title, description, category (tag), target_value (numeric), target_unit (lbs, pages, $, %, km, or custom text), start_date, deadline, and check_in_cadence (daily, weekly, monthly). shadcn/ui's Stepper or Tabs component separates the steps without a multi-page navigation.
- **Progress Input** (ui): A quick-log modal that appears in one tap or click from the goal card. The user enters their current value (or an increment to add to it), optionally adds a note, and saves. The modal auto-calculates progress_pct = Math.min(Math.round((current_value / target_value) * 100), 100). Supports both cumulative tracking (total savings $X so far) and replacement tracking (current weight X lbs, not additive).
- **Progress Visualization** (ui): A Recharts RadialBarChart on web (or fl_chart RadialBarChart on Flutter) for the circular progress ring. A Recharts LineChart or AreaChart for the sparkline history below the ring, showing logged_value over time from goal_progress_logs. react-confetti fires on first render when progress_pct reaches 100; a has_celebrated boolean on the goal prevents it from re-firing on every subsequent load.
- **Milestone Manager** (ui): An ordered list of sub-goals within a goal, each with its own title and target_value. Milestones are draggable via @dnd-kit/sortable and have a completion checkbox. Checking off the last milestone triggers a status update on the parent goal to 'completed'. The milestone list is stored in the goal_milestones table with sort_order and a FK to goals.
- **Check-In Scheduler** (backend): A Supabase Edge Function triggered by pg_cron at the user's check_in_cadence. The function sends a check-in email via Resend using a template that shows the goal title, current progress percentage, and a one-click log link. For mobile: a flutter_local_notifications alarm scheduled at the user's preferred check-in time.
- **Goals Table** (data): The primary Supabase table: id, user_id, title, description, category, target_value (numeric), target_unit, current_value (numeric), start_date, deadline, status (active/completed/paused/abandoned), check_in_cadence, and created_at. RLS restricts all operations to auth.uid() = user_id.
- **Progress Logs Table** (data): Records every update event: goal_id, user_id, logged_value, notes, and logged_at. The sparkline history chart reads from this table. An index on (goal_id, logged_at) keeps history queries fast when a goal accumulates hundreds of log entries over months.
- **Goal Insights** (backend): A Supabase RPC function that calculates the projected completion date based on average daily progress over the last 7–14 log entries. Also returns on_track / at_risk / behind status by comparing (current_value / target_value) to (days_elapsed / total_days). The RPC is called once per goal card render and cached in React Query with a 5-minute stale time.

## Data model

Three tables with RLS locked to the authenticated user. Run this in the Supabase SQL editor — it creates all tables, enables RLS, defines policies, and adds the performance indexes in one pass.

```sql
create table public.goals (
  id uuid primary key default gen_random_uuid(),
  user_id uuid references auth.users(id) on delete cascade not null,
  title text not null,
  description text,
  category text,
  target_value numeric not null,
  target_unit text not null default 'units',
  current_value numeric not null default 0,
  start_date date not null default current_date,
  deadline date,
  status text not null default 'active',
  check_in_cadence text not null default 'weekly',
  has_celebrated boolean not null default false,
  created_at timestamptz not null default now()
);

alter table public.goals enable row level security;

create policy "Users can manage own goals"
  on public.goals
  using (auth.uid() = user_id)
  with check (auth.uid() = user_id);

create table public.goal_milestones (
  id uuid primary key default gen_random_uuid(),
  goal_id uuid references public.goals(id) on delete cascade not null,
  user_id uuid references auth.users(id) on delete cascade not null,
  title text not null,
  target_value numeric,
  is_completed boolean not null default false,
  sort_order int not null default 0
);

alter table public.goal_milestones enable row level security;

create policy "Users can manage own milestones"
  on public.goal_milestones
  using (auth.uid() = user_id)
  with check (auth.uid() = user_id);

create table public.goal_progress_logs (
  id uuid primary key default gen_random_uuid(),
  goal_id uuid references public.goals(id) on delete cascade not null,
  user_id uuid references auth.users(id) on delete cascade not null,
  logged_value numeric not null,
  notes text,
  logged_at timestamptz not null default now()
);

alter table public.goal_progress_logs enable row level security;

create policy "Users can manage own progress logs"
  on public.goal_progress_logs
  using (auth.uid() = user_id)
  with check (auth.uid() = user_id);

create index goal_progress_logs_goal_time_idx
  on public.goal_progress_logs (goal_id, logged_at desc);

create index goals_user_status_idx
  on public.goals (user_id, status);
```

The has_celebrated column prevents the completion confetti from re-firing every time the goal page loads after hitting 100%. Set it to true on the first 100% detection and persist it with a Supabase UPDATE.

## Build paths

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

Best starting point for a standalone goal-tracking app: Lovable generates the Supabase tables, CRUD, and Recharts radial chart in one session; the check-in scheduler and projected completion date need a second focused prompt.

1. Create a new Lovable project and enable Lovable Cloud so the goals, goal_milestones, and goal_progress_logs tables are provisioned via the Supabase integration
2. Paste the prompt below into Agent Mode — let it build the goal list, progress ring, log modal, and milestone checklist before requesting changes
3. After the first build, send a follow-up prompt: 'Add a Supabase RPC function called get_goal_insights(goal_id uuid) that returns projected_completion_date (based on average daily progress over the last 14 log entries) and status (on_track / at_risk / behind / insufficient_data). Call it from the goal card using React Query with a 5-minute stale time.'
4. Then prompt for the check-in scheduler: 'Add a Supabase Edge Function that runs via pg_cron weekly and sends a Resend email to each user listing their active goals with current progress percentages and a link to log an update.'
5. Test the 100% completion flow: log a value that hits the target and verify react-confetti fires once, then reload and confirm it does not fire again (has_celebrated should be true)

Starter prompt:

```
Build a personalized goal setting and tracking feature. Database: three tables — goals (id, user_id, title, description, category, target_value numeric, target_unit text, current_value numeric default 0, start_date date, deadline date, status text default 'active', check_in_cadence text default 'weekly', has_celebrated boolean default false), goal_milestones (id, goal_id, user_id, title, target_value, is_completed boolean, sort_order), goal_progress_logs (id, goal_id, user_id, logged_value numeric, notes, logged_at timestamptz). All three tables: RLS restricts all operations to auth.uid() = user_id. UI — Goal List page: a grid of goal cards. Each card shows: title, category badge, Recharts RadialBarChart showing progress_pct = Math.min(Math.round((current_value / target_value) * 100), 100), days remaining (color-coded: green > 7 days, yellow 1–7 days, red overdue), current streak of consecutive days with a log entry, and status badge (active / completed / paused / abandoned). Quick-log button on each card opens a shadcn/ui Dialog with a numeric input for logged_value, a notes textarea, and a Save button that UPSERTs current_value on the goals table and inserts a row into goal_progress_logs. Goal Detail page: same radial chart, plus a Recharts LineChart showing logged_value history over time from goal_progress_logs, a milestone checklist using @dnd-kit/sortable for reordering, and a log history list newest-first. When progress_pct first reaches 100: fire react-confetti, then set has_celebrated = true via Supabase UPDATE so confetti does not repeat on reload. Goal Creation Dialog: multi-step form with react-hook-form + zod — step 1 (title, description, category), step 2 (target_value, target_unit, current_value), step 3 (start_date, deadline, check_in_cadence). Validate: deadline must be after start_date, target_value must be positive. Edge cases: empty state when no goals exist; goals where current_value >= target_value should show status 'completed' even if not manually marked; if fewer than 3 log entries exist show 'Log more updates to see your projection' instead of a projected date. Show loading skeletons while data fetches.
```

Limitations:

- The projected completion date RPC function and the check-in email scheduler both require follow-up prompts after the initial build — bundling them into one prompt produces incomplete implementations
- Lovable may generate the confetti trigger without the has_celebrated guard, causing it to fire on every page load after 100% is reached — verify this during testing
- Lovable Cloud managed Supabase is not visible in the standard Supabase dashboard, so you cannot directly inspect goal_progress_logs — use the Lovable Cloud tab to check table data

### V0 — fit 5/10, 4–6 hours

The strongest path for a goal dashboard inside a Next.js app — V0 generates beautiful Recharts radial charts, the shadcn/ui goal card grid, and Server Actions for log insertion with high visual quality.

1. Prompt V0 with the spec below to generate the GoalCard, GoalGrid, ProgressRing, LogModal, and MilestoneList components
2. Add your Supabase credentials in V0's Vars panel (NEXT_PUBLIC_SUPABASE_URL and NEXT_PUBLIC_SUPABASE_ANON_KEY)
3. Run the SQL schema from this page in the Supabase SQL editor
4. Follow up with a second prompt: 'Add a getGoalInsights(goalId) Server Action that queries the last 14 goal_progress_logs for the goal, computes the average daily progress rate, and returns projected_completion_date and status (on_track / at_risk / behind / insufficient_data). Use date-fns for date math.'
5. For check-in emails: 'Add a Resend email sent by a Supabase Edge Function triggered weekly by pg_cron. The email lists all active goals for the user with current progress percentage and a direct link to the goal detail page.' — set this up in Supabase after deploying to Vercel

Starter prompt:

```
Build a goal setting and tracking feature for a Next.js App Router project backed by Supabase. Server Components fetch data; client components handle interaction. GoalGrid — server component that fetches all goals for the authenticated user (status = 'active') ordered by deadline ascending. GoalCard — client component showing: title, category badge, RadialBarChart from Recharts displaying progress_pct (Math.min(Math.round((current / target) * 100), 100)), days-remaining badge (green > 7, yellow 1–7, red 0 or overdue), a quick-log button. QuickLogModal — shadcn/ui Dialog client component with a controlled numeric input for logged_value and a textarea for notes; on submit calls a logProgress Server Action that inserts into goal_progress_logs and updates goals.current_value; if the update brings current_value >= target_value, also set has_celebrated = false on the first log (so confetti fires once). ProgressPage (/goals/[id]) — server component layout with: full-page RadialBarChart, Recharts LineChart of logged_value history from goal_progress_logs ordered by logged_at, MilestoneList client component (checkboxes backed by Supabase UPDATEs on goal_milestones.is_completed; when all milestones are checked, update parent goals.status to 'completed'), and a LogHistory list. GoalForm — shadcn/ui Dialog with react-hook-form + zod; fields: title, description, category (text), target_value (number > 0), target_unit, start_date, deadline (must be after start_date), check_in_cadence (daily/weekly/monthly). Celebrate on 100%: client component checks if progress_pct >= 100 and has_celebrated = false; fires react-confetti; calls a Server Action to set has_celebrated = true. Empty state: when no goals exist show a centered illustration and 'Set your first goal' CTA. Skeleton loading states for GoalGrid and ProgressPage.
```

Limitations:

- V0 does not provision Supabase — run the SQL schema manually and configure env vars in the Vercel dashboard after deploying
- The Supabase RPC for projected completion date and the pg_cron check-in scheduler need explicit follow-up prompts after the initial UI build
- V0's sandbox cannot run Server Actions with live Supabase data — deploy to Vercel to test the full flow with real database reads and writes

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

Strong mobile path: FlutterFlow's Supabase integration handles goal CRUD natively, fl_chart renders the radial progress bar, and flutter_local_notifications schedules check-in reminders on device.

1. Run the SQL schema from this page in the Supabase SQL editor to create the three tables
2. Connect Supabase in FlutterFlow under Settings → Supabase; import the goals, goal_milestones, and goal_progress_logs tables
3. Build the Goals list page: add a ListView with a Supabase query on goals filtered by user_id = currentUser.uid and status = 'active'; add an fl_chart RadialBarChart widget to each row showing (current_value / target_value) * 100
4. For the quick-log: add a BottomSheet widget with a TextField for logged_value, a TextArea for notes, and two Supabase Action buttons — one that INSERTs into goal_progress_logs and one that UPDATEs goals.current_value; after both succeed, trigger a Rebuild of the parent ListView
5. For milestone completion: add a CheckboxListTile backed by a Supabase UPDATE on goal_milestones.is_completed; add a conditional action after each UPDATE that counts incomplete milestones and sets goals.status to 'completed' if the count is 0; schedule check-in notifications using flutter_local_notifications in a custom Dart action called at app launch and whenever check_in_cadence changes

Limitations:

- Complex aggregate queries like projected completion date (requires window functions or rolling averages) are not supported in FlutterFlow's visual query builder — create a Supabase RPC function and call it as a custom Dart action
- The milestone-to-parent-goal status update requires a custom Dart action to count incomplete milestones and conditionally update the parent — FlutterFlow's Action editor does not support conditional chained database queries natively
- flutter_animate is needed for the goal completion celebration animation; it requires adding a custom widget or using FlutterFlow's built-in animation properties on a confetti widget from the widget marketplace

### Custom — fit 2/10, 1–2 weeks

Custom development is warranted only when goals require AI decomposition from natural language, automatic progress ingestion from third-party APIs, or a social accountability layer — none of which AI-tool prompting covers.

1. AI goal decomposition: user types 'I want to learn piano' and an LLM call generates SMART milestones (play a C scale, learn Fur Elise, perform for 3 people) with realistic timelines and builds the goal structure programmatically
2. Third-party progress sync: Strava for running and cycling goals (auto-log km from workout data via OAuth + webhook), Plaid for savings goals (pull account balance delta nightly), Goodreads for reading goals (track pages read via API)
3. Social accountability: share goal progress with a named accountability partner who receives check-in nudges if the goal owner misses a log entry; requires a goal_shares join table, partner notifications, and per-share permission scoping
4. Enterprise OKR platform: team and company-level goals with cascading child goals, manager approval workflows for goal creation and milestone changes, and cross-team progress dashboards

Limitations:

- Third-party API integrations (Strava, Plaid) each require a separate OAuth flow, webhook handling, and normalization logic — plan for 2–3 days per integration
- Social features introduce complex RLS: a goal shared with an accountability partner must be readable by them but not writable — requires separate RLS policies per operation type

## Gotchas

- **Progress bar jumps to 100% prematurely due to floating-point comparison** — When progress_pct is computed as (current_value / target_value) * 100, floating-point arithmetic can produce values like 99.99999... or 100.00001. A simple >= 100 check may trigger or miss the completion state incorrectly. Lovable and V0 both generate this pattern without the epsilon guard by default. Fix: Use Math.min(Math.round((current_value / target_value) * 100), 100) for display. For the completion trigger, compare with a small epsilon tolerance: Math.abs(current_value - target_value) < 0.001. Cap displayed percentage at 100 even when current_value exceeds target_value — over-achievement is common and shouldn't break the UI.
- **Projected completion date shows a past date while the goal is still active** — The projection function averages all log entries from the goal's start date. Early slow-start periods (the first week with only 1–2 small log entries) drag the average daily progress rate down to near zero, producing a projected date far in the past or effectively infinity. Including correction entries (where a user logs a lower value than a previous log) makes this worse. Fix: Use only the last 7–14 log entries for the rolling average: SELECT AVG(daily_delta) FROM (SELECT logged_value - LAG(logged_value) OVER (ORDER BY logged_at) AS daily_delta FROM goal_progress_logs WHERE goal_id = $1 ORDER BY logged_at DESC LIMIT 14). Return 'Insufficient data' if fewer than 3 log entries exist. Filter out negative deltas if using replacement-value tracking.
- **Completion confetti fires on every goal page load after hitting 100%** — react-confetti is typically triggered whenever progress_pct >= 100 in a useEffect. Without a guard, every navigation to the goal detail page fires the animation, which goes from delightful to annoying after the third visit. Lovable frequently generates this pattern without the celebration guard. Fix: Add a has_celebrated boolean column to the goals table. Set it to false on goal creation. When the completion condition first triggers: fire react-confetti, then immediately call a Server Action or Supabase UPDATE to set has_celebrated = true. In the useEffect condition, check both progress_pct >= 100 AND !goal.has_celebrated before firing the animation.
- **Milestone completion doesn't update the parent goal status** — In FlutterFlow, tapping a milestone checkbox triggers a Supabase UPDATE on goal_milestones.is_completed but no subsequent check runs to see if all milestones are now done. The parent goal stays at status = 'active' even after every milestone is checked off, which confuses users who expect the goal to auto-complete. Fix: After every milestone UPDATE, run a follow-up query: SELECT COUNT(*) FROM goal_milestones WHERE goal_id = target_goal_id AND is_completed = false. If the result is 0, execute a second UPDATE: goals SET status = 'completed' WHERE id = target_goal_id. In Lovable and V0, include this conditional logic explicitly in the prompt.
- **Weekly check-in emails sent at wrong local time** — A pg_cron job scheduled at '0 9 * * 1' fires at 9am UTC for all users simultaneously. A user who configured their check-in for Monday morning receives it at 9am UTC — which is 2am in New York or 5pm in Singapore. The mismatch makes the email feel impersonal and reduces the open rate significantly. Fix: Store the user's IANA timezone string in the profiles table. In the Edge Function, loop over users grouped by their UTC offset and send emails at the correct UTC time per group. Alternatively: use a pg_cron job that runs every hour, and the Edge Function filters users whose local time matches their preferred check-in hour. Use Luxon or date-fns-tz for timezone arithmetic inside the Edge Function.

## Best practices

- Enforce numeric targets from the start — 'lose weight' is untrackable; 'lose 10 lbs by September 1' is measurable. Use a required target_value field with a unit picker in the creation form
- Store logged_value as the absolute current value (not the delta) — this makes the history sparkline straightforward and allows users to make corrections without complex undo logic
- Show projected completion date on every goal card, not just the detail page — users make different decisions when they see they are 3 weeks behind vs 3 weeks ahead
- Use a 7–14 day rolling average for the projection rate, not all-time average — early slow starts skew the projection pessimistically and discourage users
- Send check-in reminders as a single consolidated email per user covering all active goals, not one email per goal — inbox noise from goal apps is the top reason users unsubscribe
- Cap displayed progress at 100% and allow current_value to exceed target_value — over-achievement is common (e.g., saving $6,000 toward a $5,000 goal) and should show 100% not 120%, which looks broken
- Include an 'abandon' status alongside 'completed' and 'paused' — users who can cleanly remove goals they no longer care about stay engaged with their remaining ones longer than users who must look at stale goals
- Persist the has_celebrated flag immediately on the first 100% detection — do not wait for a separate user action — so the confetti never repeats on reload

## Frequently asked questions

### What makes a goal 'SMART' and how do I enforce that in the UI?

SMART stands for Specific, Measurable, Achievable, Relevant, Time-bound. The measurable and time-bound parts are enforceable in the form: require a numeric target_value with a unit, and require a deadline date that is after the start date. Validate both at the zod schema level so the error appears before submission. You cannot enforce 'achievable' or 'relevant' programmatically — those are judgment calls — but you can show a warning if the projected daily progress rate required to hit the target by the deadline seems unusually high.

### Can I track progress in non-numeric goals like habit formation?

Yes — use binary goals where the target_value is 1 and each log entry sets current_value to 1 (done). Or use a count-based goal (e.g., 'meditate 30 times this month' with target_value = 30 and each log increments by 1). The same table structure handles both. For pure habit tracking with a streak counter, the personalized-daily-routines feature is a better fit.

### How do I add sub-goals or milestones?

The goal_milestones table handles this: each milestone has a goal_id FK, a title, an optional target_value (useful for numeric checkpoints like 'hit 50% of savings target'), and an is_completed boolean. Display them as a checklist on the goal detail page using @dnd-kit/sortable for reordering. When all milestones are checked, run a query to count incomplete ones and update the parent goal status to 'completed' if zero remain.

### How do I get notified to update my progress?

Set the check_in_cadence field (daily, weekly, monthly) when creating the goal. A Supabase Edge Function triggered by pg_cron reads the cadence for each user and sends a Resend email at the scheduled time. The email includes current progress percentage and a direct link to the log modal. For mobile apps, flutter_local_notifications schedules an on-device alarm that works even when the app is closed.

### What's the difference between this and a to-do list?

A to-do list tracks discrete tasks (done or not done). A goal-tracking feature tracks continuous progress toward a numeric target over time. The key distinction is the logged history: goal tracking records every intermediate data point so you can see trajectory and project completion dates. A to-do list does not. Build a to-do list when your users need task management; build goal tracking when they need to see how far they've come.

### How do I archive completed goals without deleting them?

Use the status column — 'completed' and 'abandoned' goals remain in the database but are excluded from the active goals list view by default. Add a toggle to the UI ('Show archived goals') that re-runs the query without the status = 'active' filter. This preserves the full history log for completed goals, which users often want to look back on.

### Can I import goals from another app?

The simplest approach: build a CSV import flow. The CSV template has columns for title, target_value, target_unit, deadline, and category. Parse the file client-side using PapaParse, preview the parsed goals in a table, then bulk-insert into Supabase with a single Server Action. Full-file format imports from apps like Notion or Habitica require custom parsers specific to their export formats.

### Can I share my goals publicly or with friends?

Add an is_public boolean column to goals and update the RLS SELECT policy: allow reads when is_public = true in addition to the existing auth.uid() = user_id rule. Public goals get a shareable URL like /goals/{goal_id} that shows progress without requiring login. For a private accountability partner, add a goal_shares table with (goal_id, shared_with_user_id) and extend the RLS policy to allow reads for users in this table.

---

Source: https://www.rapidevelopers.com/app-features/personalized-goal-setting-and-tracking
© RapidDev — https://www.rapidevelopers.com/app-features/personalized-goal-setting-and-tracking
