Skip to main content
RapidDev - Software Development Agency
how-to-build-v030-60 minutes

How to Build Habit tracker with V0

Build a streak-based habit tracker with V0 using Next.js, Supabase for habit data, and a custom heatmap calendar showing daily completions. You'll create a today checklist, streak counters, and completion statistics — all in about 30-60 minutes without touching a terminal.

What you'll build

  • Today's habit checklist with one-tap completion toggle and streak indicators using Card and Checkbox
  • Weekly overview row showing completed vs missed days for each habit
  • Streak counter badges with fire icon that auto-calculate consecutive completion days
  • Completion heatmap calendar styled like a GitHub contribution graph using colored grid cells
  • Habit management page with drag-to-reorder, edit, and archive functionality
  • Overall completion rate and per-habit statistics on a dedicated stats page
Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Beginner9 min read30-60 minutesV0 FreeApril 2026RapidDev Engineering Team
TL;DR

Build a streak-based habit tracker with V0 using Next.js, Supabase for habit data, and a custom heatmap calendar showing daily completions. You'll create a today checklist, streak counters, and completion statistics — all in about 30-60 minutes without touching a terminal.

What you're building

Habit tracking helps people build consistency. By checking off daily habits and seeing streaks grow over time, users stay motivated. A heatmap calendar makes months of progress visible at a glance.

V0 makes this fast by generating the checklist UI, streak logic, and heatmap calendar from prompts. Supabase stores habits, completions, and streak data. The beginner-friendly architecture keeps everything simple with just three pages.

The architecture uses Next.js App Router with a client component for the interactive today checklist, Server Components for the stats page, Server Actions for toggling completions and recalculating streaks, and a custom CSS grid for the heatmap calendar.

Final result

A habit tracking app with daily checklist, streak counters, habit management, completion heatmap calendar, and overall statistics.

Tech stack

V0AI Code Generator
Next.jsFull-Stack Framework
Tailwind CSSStyling
shadcn/uiComponent Library
SupabaseDatabase & Auth

Prerequisites

  • A V0 account (free tier works for this project)
  • A Supabase project (free tier works — connect via V0's Connect panel)
  • Basic understanding of daily habits and streaks
  • No coding experience needed — perfect for beginners

Build steps

1

Set up the project and habit database schema

Open V0 and create a new project. Connect Supabase via the Connect panel. Create the schema for habits, completions, and streaks.

prompt.txt
1// Paste this prompt into V0's AI chat:
2// Build a habit tracking app. Create a Supabase schema with:
3// 1. habits: id (uuid PK), user_id (uuid FK to auth.users), name (text), description (text), icon (text), color (text default '#22c55e'), frequency (text check in 'daily','weekdays','weekly' default 'daily'), target_per_day (int default 1), position (int), is_archived (boolean default false), created_at (timestamptz)
4// 2. completions: id (uuid PK), habit_id (uuid FK to habits), date (date), count (int default 1), created_at (timestamptz), unique(habit_id, date)
5// 3. streaks: id (uuid PK), habit_id (uuid FK to habits), current_streak (int default 0), longest_streak (int default 0), last_completed_date (date)
6// Add RLS: users see only their own habits and completions.
7// Create a trigger that auto-creates a streaks row when a new habit is inserted.

Pro tip: Use Design Mode (Option+D) to customize habit card colors and the heatmap palette — perfect for beginners since visual tweaks cost no credits.

Expected result: Database schema created with habits, completions, and streaks tables plus RLS policies restricting access to the authenticated user.

2

Build the today page with habit checklist

Create the main page showing today's habits as a checklist. Each habit shows its name, streak count, and a toggle to mark it complete. Include a weekly overview showing the last 7 days.

prompt.txt
1// Paste this prompt into V0's AI chat:
2// Build a habit tracker homepage at app/page.tsx as a 'use client' component.
3// Requirements:
4// - Today's date prominently displayed at top
5// - List of active habits as Card components, each with:
6// - Checkbox for completion toggle (optimistic UI)
7// - Habit name and icon
8// - Badge showing current streak with fire emoji
9// - Color indicator matching the habit's color
10// - Weekly overview below: 7-day row per habit showing filled/empty circles
11// - Overall completion rate for today (e.g., "4/6 habits done")
12// - "Add Habit" Button linking to /habits
13// - When Checkbox is toggled, call a Server Action that upserts into completions
14// and recalculates the streak in the streaks table
15// - Use Card for each habit and Badge for streak count

Expected result: The homepage shows today's habits as cards with checkboxes, streak badges, and a weekly overview of the last 7 days.

3

Create the streak calculation Server Action

Build the Server Action that toggles a habit completion and recalculates the current streak. The streak logic counts consecutive days backwards from today.

prompt.txt
1// Paste this prompt into V0's AI chat:
2// Build a Server Action at app/actions/habits.ts for toggling habit completion.
3// Requirements:
4// - toggleCompletion(habitId: string, date: string) Server Action
5// - If completion exists for that habit+date, delete it. Otherwise upsert with count=1.
6// - After toggling, recalculate the streak:
7// Query completions for this habit ordered by date DESC
8// Count consecutive days backwards from today (break on first missing day)
9// Update streaks table: current_streak = calculated value
10// If current_streak > longest_streak, update longest_streak too
11// - Use Zod to validate habitId (uuid) and date (string)
12// - revalidatePath('/') after the update
13// - Also create addHabit and updateHabit Server Actions with Zod validation

Pro tip: The streak recalculation runs in a single Server Action call, keeping the UI responsive with optimistic updates on the client side.

Expected result: Server Actions handle completion toggling with automatic streak recalculation, updating both the completions and streaks tables.

4

Build the completion heatmap calendar

Create the stats page with a GitHub-style heatmap calendar showing completion frequency over 365 days. Include per-habit streaks and overall statistics.

prompt.txt
1// Paste this prompt into V0's AI chat:
2// Build a stats page at app/stats/page.tsx.
3// Requirements:
4// - Completion heatmap: a 365-day CSS grid (like GitHub contribution graph)
5// Each cell colored by completion count for that day:
6// 0 = gray-100, 1 = green-200, 2 = green-400, 3+ = green-600
7// Wrap the heatmap in a 'use client' component, keep page as Server Component
8// - Month labels above the heatmap grid
9// - Streak leaderboard: Card for each habit showing current and longest streak
10// sorted by current streak DESC, with Badge for streak count
11// - Overall stats Cards: total completions, current longest streak, best day, completion rate this month
12// - Select to filter heatmap by specific habit or show all habits combined
13// - Use Card for stat containers and Badge for streak indicators

Expected result: The stats page shows a 365-day heatmap calendar with green shading, per-habit streak rankings, and overall completion statistics.

5

Add habit management and archiving

Build the habit management page where users create, edit, reorder, and archive habits. Archived habits disappear from the today checklist but keep their history.

prompt.txt
1// Paste this prompt into V0's AI chat:
2// Build a habits management page at app/habits/page.tsx.
3// Requirements:
4// - List all active habits in a reorderable list (drag handle icon)
5// - Each habit row: icon, name, frequency Badge, color circle, edit Button, archive Switch
6// - "Add Habit" Button opening Dialog with:
7// Input for name, Input for description, Select for icon (emoji picker),
8// color picker (6 preset color buttons), Select for frequency (daily/weekdays/weekly)
9// - Edit: same Dialog pre-filled with existing values
10// - Archive toggle: Switch that sets is_archived=true, habit disappears from today page
11// - Archived section at bottom (collapsed by default) showing archived habits with restore Button
12// - Server Actions for create, update, reorder (update position), and archive
13// - Use Dialog for the form, Switch for archive toggle, Badge for frequency

Expected result: Users can create habits with custom names, icons, and colors, reorder them by dragging, and archive habits while preserving their completion history.

Complete code

app/actions/habits.ts
1'use server'
2
3import { createClient } from '@/lib/supabase/server'
4import { revalidatePath } from 'next/cache'
5import { z } from 'zod'
6
7const toggleSchema = z.object({
8 habitId: z.string().uuid(),
9 date: z.string().regex(/^\d{4}-\d{2}-\d{2}$/),
10})
11
12export async function toggleCompletion(input: z.infer<typeof toggleSchema>) {
13 const supabase = await createClient()
14 const { data: { user } } = await supabase.auth.getUser()
15 if (!user) throw new Error('Unauthorized')
16
17 const { habitId, date } = toggleSchema.parse(input)
18
19 const { data: existing } = await supabase
20 .from('completions')
21 .select('id')
22 .eq('habit_id', habitId)
23 .eq('date', date)
24 .single()
25
26 if (existing) {
27 await supabase.from('completions').delete().eq('id', existing.id)
28 } else {
29 await supabase.from('completions').insert({ habit_id: habitId, date })
30 }
31
32 const { data: completions } = await supabase
33 .from('completions')
34 .select('date')
35 .eq('habit_id', habitId)
36 .order('date', { ascending: false })
37
38 let streak = 0
39 const today = new Date()
40 for (let i = 0; i < 365; i++) {
41 const checkDate = new Date(today)
42 checkDate.setDate(today.getDate() - i)
43 const dateStr = checkDate.toISOString().split('T')[0]
44 if (completions?.some((c) => c.date === dateStr)) {
45 streak++
46 } else break
47 }
48
49 const { data: streakRow } = await supabase
50 .from('streaks')
51 .select('longest_streak')
52 .eq('habit_id', habitId)
53 .single()
54
55 await supabase
56 .from('streaks')
57 .upsert({
58 habit_id: habitId,
59 current_streak: streak,
60 longest_streak: Math.max(streak, streakRow?.longest_streak ?? 0),
61 last_completed_date: existing ? null : date,
62 }, { onConflict: 'habit_id' })
63
64 revalidatePath('/')
65 revalidatePath('/stats')
66}

Customization ideas

Add habit categories

Group habits by category (Health, Work, Personal) with colored section headers and per-category completion rates.

Add reminders

Let users set a daily reminder time per habit that triggers a browser notification using the Notification API.

Add social accountability

Let users share their streak milestones with friends via public profile pages showing heatmaps.

Add habit templates

Offer pre-built habit bundles (Morning Routine, Fitness, Mindfulness) that users can import with one click.

Common pitfalls

Pitfall: Not using optimistic UI for the completion toggle

How to avoid: Toggle the checkbox state immediately on click, then call the Server Action. Revert if the action fails.

Pitfall: Recalculating streaks by scanning all completions on every page load

How to avoid: Store the current streak in a dedicated streaks table and only recalculate when a completion is toggled.

Pitfall: Forgetting the unique constraint on completions(habit_id, date)

How to avoid: Add a unique constraint on (habit_id, date) and use upsert instead of insert for completion toggling.

Best practices

  • Store streaks in a dedicated table updated on each toggle rather than computing from all completions on every page load.
  • Use the unique constraint on completions(habit_id, date) to prevent duplicate entries from rapid toggling.
  • Use Design Mode (Option+D) to customize habit card colors and heatmap shading for free — ideal for beginners.
  • Implement optimistic UI for the completion checkbox so the toggle feels instant even with server-side validation.
  • Use RLS policies with auth.uid() = user_id on all tables so each user sees only their own habits.
  • Cache the heatmap data with ISR revalidation since historical completions don't change frequently.

AI prompts to try

Copy these prompts to build this project faster.

ChatGPT Prompt

I'm building a habit tracker with Next.js and Supabase. I need to calculate streaks efficiently — counting consecutive days backwards from today where a completion exists. Show me a PostgreSQL query that does this without scanning all rows, how to store the result in a streaks table, and how to update it atomically when a completion is toggled.

Build Prompt

Build the today's habits checklist component for a habit tracker. Create a 'use client' component that fetches active habits with their streak counts. Each habit renders as a Card with a Checkbox that toggles completion via a Server Action (optimistic UI). Show a Badge with the current streak and fire icon. Below the checklist, render a 7-day weekly overview per habit using filled/empty circles. Show overall completion rate at the top.

Frequently asked questions

Can I build this with the free V0 plan?

Yes. The free tier provides enough credits for the core checklist, heatmap, and habit management pages. Use Design Mode (Option+D) for visual tweaks at no cost.

How do streaks work?

A Server Action counts consecutive days backwards from today where a completion exists. The streak is stored in a dedicated streaks table and updated only when you toggle a habit, not on every page load.

Can I track habits that aren't daily?

Yes. The frequency column supports daily, weekdays, and weekly. The streak calculation adjusts to only count applicable days based on the frequency setting.

How does the heatmap calendar work?

A CSS grid renders 365 cells, one per day. Each cell queries the completions table for that date and is colored from gray (no completions) to dark green (3+ completions), similar to GitHub's contribution graph.

Can I customize habit colors?

Yes. Each habit has a color field. The Card component uses this color for the left border accent, and the heatmap can filter by habit to show that habit's specific color.

How do I deploy?

Publish via V0's Share menu. Set NEXT_PUBLIC_SUPABASE_ANON_KEY and NEXT_PUBLIC_SUPABASE_URL for client-side completion toggling, and SUPABASE_SERVICE_ROLE_KEY for Server Actions.

Can RapidDev help build a custom habit tracking app?

Yes. RapidDev has built 600+ apps including habit and wellness platforms with social features, push notifications, and Apple Health integration. Book a free consultation to discuss your vision.

RapidDev

Talk to an Expert

Our team has built 600+ apps. Get personalized help with your project.

Book a free consultation

Need help building your app?

Our experts have built 600+ apps and can accelerate your development. Book a free consultation — no strings attached.

Book a free consultation

We put the rapid in RapidDev

Need a dedicated strategic tech and growth partner? Discover what RapidDev can do for your business! Book a call with our team to schedule a free, no-obligation consultation. We'll discuss your project and provide a custom quote at no cost.