# How to Add Personalized Daily Routines to Your App (Copy-Paste Prompts Included)

- Tool: App Features
- Last updated: July 2026

## TL;DR

A personalized daily routines feature needs three things: a drag-and-drop routine builder backed by a Supabase table, a streak engine that counts consecutive days in the user's local timezone, and push notification reminders at a scheduled time. With Lovable or FlutterFlow you can ship the core in 4–6 hours for $0/month on the free tier. Costs appear only at 10K+ users when Supabase Pro ($25/mo) is needed.

## What a Personalized Daily Routines Feature Actually Is

A daily routines feature lets users build their own sequence of habits — morning workouts, reading sessions, medication reminders, journaling — and tracks whether they complete each one every day. The key product decisions are how streaks work (UTC vs local timezone is a known pain point), how completion is logged (once per day per routine or multiple times), and what happens when a routine is missed. The drag-and-drop builder, streak counter, and weekly heatmap are the three UI components users compare between apps. The reminder scheduling is what makes the feature sticky: a push notification at the right time pulls users back in. AI tools handle the CRUD and visualization reliably; the streak timezone logic and server-side notifications are where builds break.

## Anatomy of the Feature

Seven components. The CRUD and chart layers are what AI tools generate first; the streak engine and notification scheduler are where the complexity lives and where builds most commonly need a second pass.

- **Routine Builder UI** (ui): Drag-and-drop list built with @dnd-kit/sortable on web or ReorderableListView in Flutter. Each item has a name field, duration picker (minutes), an icon picker from a predefined set, and a reminder time selector. The sort_order column in the database persists the user's chosen sequence.
- **Streak Engine** (backend): A Supabase Edge Function or Server Action that computes the streak by querying routine_logs for consecutive calendar days in the user's local timezone. The result is cached in the routines.current_streak and routines.longest_streak columns so the UI reads a single integer rather than re-aggregating logs on every load.
- **Daily Completion Grid** (ui): A calendar heatmap showing completion intensity per day. On web: react-calendar-heatmap library using data aggregated by date from routine_logs. On Flutter: table_calendar with a custom cell builder. Aggregation groups logs by the user's local date, not UTC.
- **Reminder Scheduler** (service): On web: a pg_cron job triggers a Supabase Edge Function at the correct UTC time per user; the Edge Function sends a Web Push notification via the web-push npm package. On Flutter: flutter_local_notifications schedules an on-device alarm tied to the user's local time, removing the server dependency entirely. The scheduled_time is stored in the routines table as a time column; timezone is pulled from the user's profile.
- **Routines Table** (data): The primary Supabase table holding each user's routines: name, icon, sort_order, duration_minutes, reminder_time (time type), is_active flag, and cached streak values. RLS restricts all operations to the row owner via auth.uid() = user_id.
- **Routine Log Table** (data): Records every completion event: routine_id, user_id, completed_at timestamptz, duration_actual in minutes, and optional notes. A composite index on (user_id, routine_id, completed_at) keeps history queries fast even when users accumulate thousands of entries.
- **Progress Summary Widget** (ui): A weekly/monthly completion percentage displayed as a bar chart (Recharts on web, fl_chart on Flutter). Shows per-routine completion rates so users can identify which habits they struggle with. Clicking a routine in the chart drills down to its individual log history.

## Data model

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

```sql
create table public.routines (
  id uuid primary key default gen_random_uuid(),
  user_id uuid references auth.users(id) on delete cascade not null,
  name text not null,
  icon text,
  sort_order int not null default 0,
  duration_minutes int not null default 5,
  reminder_time time,
  is_active boolean not null default true,
  current_streak int not null default 0,
  longest_streak int not null default 0,
  created_at timestamptz not null default now()
);

alter table public.routines enable row level security;

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

create table public.routine_logs (
  id uuid primary key default gen_random_uuid(),
  user_id uuid references auth.users(id) on delete cascade not null,
  routine_id uuid references public.routines(id) on delete cascade not null,
  completed_at timestamptz not null default now(),
  duration_actual int,
  notes text
);

alter table public.routine_logs enable row level security;

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

create index routine_logs_user_routine_date_idx
  on public.routine_logs (user_id, routine_id, completed_at desc);

create index routine_logs_user_date_idx
  on public.routine_logs (user_id, completed_at desc);
```

The second index on (user_id, completed_at) powers the weekly heatmap query that groups completions by date across all routines. The first index powers per-routine streak calculation.

## Build paths

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

Best starting point for a web app: Lovable generates the Supabase tables, CRUD actions, and streak logic in one session; the prompt below gets you 80% of the feature in the first run.

1. Create a new Lovable project and enable Lovable Cloud so the routines and routine_logs tables are provisioned automatically via the Supabase integration
2. Paste the prompt below into Agent Mode — let it build the routine list, completion UI, streak counter, and weekly chart before asking for changes
3. After the first build, prompt separately: 'Add a Supabase Edge Function that runs via pg_cron at each user's local reminder_time and sends a Web Push notification using the web-push library' — this is too complex to bundle into the initial prompt
4. Test the drag-and-drop sort by reordering routines and refreshing the page; confirm sort_order persists in Supabase
5. Publish and test the completion checkbox animation and streak increment on the live URL

Starter prompt:

```
Build a personalized daily routines feature. Database tables: routines (id, user_id, name, icon, sort_order, duration_minutes, reminder_time, is_active, current_streak, longest_streak) and routine_logs (id, user_id, routine_id, completed_at, duration_actual, notes) — both with RLS restricting all operations to auth.uid() = user_id. UI: a draggable list of the user's active routines using @dnd-kit/sortable; after drag-end, batch-update sort_order for all affected rows in Supabase. Each routine card shows the name, icon, duration, a completion checkbox, and the current_streak count with a flame icon. Checking the box inserts a row into routine_logs and increments current_streak; checking it again on the same calendar day (user's local timezone) shows 'already logged today' and does not insert a duplicate. Add a weekly completion bar chart using Recharts showing completion rate per routine for the past 7 days. Include a 'New Routine' form with name, icon picker (emoji), duration in minutes, and reminder time. Handle edge cases: if user_id has no routines show an empty state with a prompt to add the first one; if a routine has never been completed show streak as 0 and no heatmap data. Show a loading skeleton while data fetches. Timezone: store completed_at in UTC, but group by date using the user's browser timezone (Intl.DateTimeFormat().resolvedOptions().timeZone) for streak and chart calculations.
```

Limitations:

- Push notification scheduling via pg_cron requires a separate Supabase Edge Function prompt — Lovable may default to a client-side setTimeout that stops working when the browser tab is closed
- Drag-and-drop reorder sometimes generates optimistic UI only without the Supabase batch UPDATE — verify sort_order persists after a page refresh
- Lovable Cloud managed Supabase is not visible in the standard Supabase dashboard, so you cannot inspect routine_logs directly in the Table Editor

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

Good path when the routines feature lives inside a larger Next.js app — V0 generates the cleanest Recharts heatmap and shadcn/ui drag-and-drop list, but streak logic needs a follow-up prompt.

1. Prompt V0 with the spec below to generate the RoutineList, RoutineCard, and WeeklyChart components
2. Add your Supabase environment variables 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 to create the routines and routine_logs tables
4. Follow up with a second prompt: 'Add a Server Action that calculates the streak for a given routine_id by querying routine_logs grouped by date in the user's timezone and returns the consecutive-day count' — V0 needs this scoped separately
5. Publish and test the drag-and-drop sort on the live Vercel URL

Starter prompt:

```
Build a daily routines tracker feature for a Next.js App Router project using Supabase. Components: RoutineList (client component) — a draggable list of routines using @dnd-kit/sortable; on drag-end call a Server Action that batch-upserts sort_order for all reordered rows. RoutineCard — shows routine name, emoji icon, duration in minutes, a completion checkbox (checked if a routine_log exists for today in the user's local timezone), current_streak with flame icon, and a delete button. CheckIn Server Action — inserts into routine_logs (user_id, routine_id, completed_at: new Date().toISOString()); if a log already exists for today in the user's timezone return { alreadyLogged: true } instead of inserting. WeeklyChart — a Recharts BarChart showing completion percentage per routine for the last 7 days, data fetched via a Server Component. RoutineForm — shadcn/ui Dialog with name input, emoji picker, duration number input, and reminder time picker; submits to a createRoutine Server Action. Skeleton loading state for RoutineList. Empty state when no routines exist. Handle the date grouping by timezone: use Intl.DateTimeFormat(undefined, { timeZone: userTimezone }).format(date) to convert UTC completed_at to local date strings before comparing.
```

Limitations:

- V0 is component-focused — the streak recalculation logic and the pg_cron Edge Function for push notification scheduling need explicit follow-up prompts beyond the initial UI generation
- Supabase is not auto-provisioned by V0; you must run the SQL schema manually and configure env vars in the Vercel dashboard
- Recharts drag-and-drop and heatmap are separate libraries — V0 may occasionally mix up react-beautiful-dnd (deprecated) with @dnd-kit; verify the import in the generated code

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

The strongest path for a mobile-first routines app: FlutterFlow's Supabase queries handle CRUD natively, flutter_local_notifications gives reliable on-device alarms, and table_calendar renders the completion heatmap without custom code.

1. Add the routines and routine_logs tables in Supabase and paste the SQL schema from this page into the Supabase SQL editor
2. In FlutterFlow, connect Supabase under Settings → Supabase and import both tables; FlutterFlow auto-generates the query types
3. Build the RoutineList page: add a ListView backed by a Supabase query on routines filtered to the logged-in user; wrap each row in ReorderableListView and add an on-reorder action that updates sort_order via a Supabase batch update custom action
4. For the completion checkbox: add an Action to the CheckBox widget that queries routine_logs for today's date in the user's timezone, inserts a new log row if none exists, then triggers a Rebuild of the streak display widget
5. Set up flutter_local_notifications as a custom action: schedule a notification at reminder_time for each active routine; call this custom action on app launch and whenever a routine's reminder_time changes; add the iOS permission request in Settings → Permissions with a usage message explaining why reminders are needed

Limitations:

- Timezone-correct streak calculation (grouping logs by local date, not UTC) requires a Supabase RPC function or custom Dart action — FlutterFlow's visual query builder compares timestamps directly in UTC
- The completion heatmap via table_calendar with a custom cell builder requires writing custom Dart code — it is not available as a built-in FlutterFlow widget
- Drag-and-drop reorder with persistent sort_order requires a custom Dart action for the batch Supabase upsert; the visual Action editor does not support looping over a list of updated rows

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

Custom development is warranted only when routines need AI-generated habit recommendations, wearable sync, or multi-user shared routines — features AI tools cannot handle without significant architecture work.

1. AI habit coach: integrate an LLM API to decompose user goals into suggested routines with realistic time allocations; requires prompt engineering, streaming responses, and a feedback loop to refine suggestions over time
2. Wearable sync: Apple HealthKit or Google Fit integration to auto-complete routines like 'Morning walk' when step count data confirms the activity; requires native SDK access not available in web apps
3. Team routines: shared routine libraries where family members or teammates see each other's completions; requires a routines_members join table and per-member RLS policies
4. Enterprise wellness platforms: admin-defined routine libraries, compliance reporting dashboards, and HRIS integration for employee wellness programs

Limitations:

- LLM API costs accumulate quickly if habit recommendations are generated on every session — cache suggestions aggressively and only regenerate on explicit user request
- Wearable sync via HealthKit requires a native iOS app build; web apps cannot access HealthKit data even as a PWA

## Gotchas

- **Streak shows 0 even though the user completed the routine yesterday** — The streak calculation compares completed_at dates in UTC. A user who finishes their routine at 11pm in New York (UTC-5) generates a log timestamped 4am the next UTC day. The consecutive-day check sees a gap where there is none, and resets the streak to 0. This is the most common streak bug — and the one users are most likely to complain about. Fix: Convert completed_at to the user's local timezone before grouping by date. In PostgreSQL: SELECT completed_at AT TIME ZONE 'America/New_York' AS local_date. Store the user's IANA timezone string (from Intl.DateTimeFormat().resolvedOptions().timeZone on the client) in the profiles table. In the Edge Function use Luxon or date-fns-tz to perform all date math in the user's timezone.
- **Drag-and-drop reorder works visually but reverts on page refresh** — AI tools frequently generate the @dnd-kit optimistic UI update (which instantly shows the new order on screen) but omit the Supabase batch UPDATE that persists sort_order. The in-memory state looks correct but the database still has the old order, so a refresh reverts the list. Fix: After drag-end, call Supabase with an array of { id, sort_order } pairs using upsert: await supabase.from('routines').upsert(reorderedItems.map((r, i) => ({ id: r.id, sort_order: i }))); Verify persistence by reordering, refreshing, and confirming the new order holds.
- **Push notification reminder fires at 9am UTC, not 9am local time** — pg_cron schedules in UTC. If the Edge Function queries routines where reminder_time = current_time without timezone conversion, it fires for users whose UTC wall clock matches, regardless of local time. A user in Tokyo who sets a 7am reminder gets it at 7am UTC (4pm Tokyo), hours late. Fix: Store the user's timezone in the profiles table. In the pg_cron trigger, group users by their UTC offset and schedule separate Edge Function calls per timezone group — or have a single Edge Function that runs every minute, filters routines where the local time matches reminder_time for each user's timezone, and sends only the matching notifications.
- **Lovable generates setTimeout for reminders instead of a cron job** — Without explicit instruction, Lovable often implements notification reminders using a client-side setTimeout. This appears to work in the preview but stops firing the moment the browser tab is closed, backgrounded, or the device sleeps — which defeats the entire purpose of a routine reminder. Fix: In your Lovable prompt, explicitly specify: 'Do not use setTimeout for notification reminders. Use a Supabase Edge Function triggered by pg_cron to send Web Push notifications server-side.' The Edge Function approach works regardless of whether the user's browser is open.
- **Completion heatmap shows empty cells on days that had completions** — react-calendar-heatmap expects values in the format { date: 'YYYY-MM-DD', count: N }. If completed_at timestamps are grouped in UTC before converting to local dates, a completion at 11pm local time on Monday appears as a Tuesday UTC date, creating an empty cell for Monday and an inflated count for Tuesday. Fix: Normalize all completed_at values to the user's local timezone before grouping by date: use date-fns-tz format(toZonedTime(completed_at, userTimezone), 'yyyy-MM-dd') for each log entry before aggregating. Do this server-side in the Supabase RPC function or Edge Function, not in the React component.

## Best practices

- Store the user's IANA timezone string in the profiles table at signup so all streak and heatmap calculations run in the correct timezone from day one
- Batch-upsert sort_order after drag-and-drop in a single Supabase call — individual UPDATE calls per row create N round trips and visible lag on slow connections
- Cache current_streak in the routines table and recompute it nightly via Edge Function cron, not on every page load — streak recalculation over thousands of log entries is expensive
- Log both completed_at (UTC) and duration_actual (minutes) in routine_logs — duration data enables 'time spent per habit' insights that significantly increase long-term engagement
- Use flutter_local_notifications for on-device alarms in Flutter apps rather than server-push for reminders — on-device alarms fire reliably even without an internet connection and require no server infrastructure
- Prevent double-logging by checking whether a routine_log already exists for today's local date before inserting; return a clear 'already logged today' state rather than silently discarding the duplicate
- Show the longest_streak alongside current_streak on each routine card — users are more motivated by their personal best than today's count alone
- Test the streak logic specifically at midnight boundary conditions by temporarily setting a test user's timezone to one that is 12+ hours ahead of UTC and verifying logs on day boundaries are attributed correctly

## Frequently asked questions

### Can routines repeat on specific days of the week rather than daily?

Yes — add a repeat_days integer array column to the routines table (where 0 = Sunday, 1 = Monday, etc.). Filter which routines show on a given day by checking if today's day number is in the array. The streak logic needs to account for scheduled days only, so a Monday-only routine that was completed last Monday has a streak of 1 regardless of what happened Tuesday through Sunday.

### How do I prevent the streak counter from breaking due to timezone differences?

Store the user's IANA timezone string (from Intl.DateTimeFormat().resolvedOptions().timeZone in the browser) in your profiles table at signup. Always convert completed_at from UTC to the user's local timezone before grouping by date. In PostgreSQL: SELECT (completed_at AT TIME ZONE user_timezone)::date as local_date. Running streak comparisons in UTC is the single most common cause of streak resets that shouldn't happen.

### What happens if I miss a day — can I mark a backdated completion?

You can allow it by accepting a completed_at parameter in the log insert instead of defaulting to now(). Show a date picker in the completion flow with a note like 'Logging a past completion.' Decide upfront whether backdated completions count for streak restoration — many habit apps deliberately block backdating to maintain streak integrity, while others allow it with a visual indicator marking the entry as manually logged.

### Can routines be grouped into different areas like morning, evening, or fitness?

Add a category text column to the routines table and a category filter in the UI. Display routines in collapsible sections by category on the main screen. Categories also enable category-level completion stats (how often does the user finish all morning routines?) which are a useful insight to surface in the weekly summary.

### How do I export routine data?

The simplest path: a Server Action that queries routine_logs for a date range, formats it as CSV with columns (routine_name, completed_at, duration_actual, notes), and returns it as a file download. For Supabase apps, you can also expose a read-only API route that returns the data as JSON for users who want to import it into spreadsheets or other tools.

### Can the app integrate with Apple Health or Google Fit?

Only in native mobile apps — HealthKit requires an iOS native build (React Native, Swift, or Flutter with the health package), and Google Fit requires Android-native access. Web apps including PWAs cannot read step count, sleep data, or workout data from these platforms. If wearable sync is core to your feature, plan for a Flutter or React Native build from the start.

### How do I add an AI coach that suggests routines?

On the back end, collect the user's stated goals and their past completion rates per routine. Pass this context to an LLM API call (Claude or GPT-4o) with a prompt that asks for 3–5 SMART routine suggestions with realistic durations. Return the suggestions as a pre-filled 'Add Routine' flow the user can accept, edit, or dismiss. Cache the suggestions so you aren't calling the LLM API on every visit.

### Can users share their routine with another user or make it public?

Add an is_public boolean column to routines. Update the Supabase RLS SELECT policy to allow reads when is_public = true, in addition to the existing owner-only access. Add a shareable link like /routines/{routine_id} that fetches the public routine data without requiring login. Other users can then import the routine to their own account by inserting a copy into their own routines table.

---

Source: https://www.rapidevelopers.com/app-features/personalized-daily-routines
© RapidDev — https://www.rapidevelopers.com/app-features/personalized-daily-routines
