Skip to content
Case StudyMarch 5, 2026· 9 min read

I Built a Fitness Tracker with Next.js and Convex. No App Was Good Enough.

Workout logger, macro tracking, progress graphs and training plan in one app. 9 Convex tables, TypeScript everywhere. Why I didn't just use Excel.

I Built a Fitness Tracker with Next.js and Convex. No App Was Good Enough.

Why Build Your Own Fitness App?

Because MyFitnessPal is for food, Strong is for workouts, Apple Health is for steps and weight, and nowhere do I have everything together with my training plan.

My situation: 194 cm, 121 kg, goal to reach 95-100 kg by September 2026. That means tracking food, workouts, weight, sleep and body measurements — and seeing it all in one place.

What the App Does

/today — What's Today

Main page. I open it in the morning and see:

  • Today's workout from the plan (which split, which exercises, how many sets)
  • Nutrition targets (2800 kcal, 190-210g protein)
  • Daily minimum checklist (food, training, sleep, water)
  • How much I've done and what's missing

/workout — Workout Logger

I pick today's workout from the plan (Upper A, Lower B, Legs+Shoulders...). For each exercise I log sets — weight and reps. The app remembers last values, so I don't need to look up what I lifted last time.

/food — Macros

I log food with macros. I have favorite meals and recipes, so lunch with 4 ingredients takes two clicks. I see running totals of calories, protein, carbs and fat.

/body — Body Measurements

Daily: weight and sleep quality. Monthly: waist, chest, biceps, thighs circumference. The app calculates a 7-day weight average to smooth out daily fluctuations.

/history — Charts

Trend charts: weight over time, macro adherence, progression in individual exercises. This is where I see if I'm heading the right direction.

Why Convex?

I tried Supabase, Firebase, plain Postgres. Convex won for these reasons:

1. Realtime out of the box. No WebSocket setup, no subscribes. Write a query, data updates live. 2. TypeScript end-to-end. Schema in TypeScript, functions in TypeScript, frontend in TypeScript. No ORM, no extra types. 3. Transactions are built-in. Mutations are automatically transactional. No race conditions when logging a set and updating progress at the same time.

Database Schema — 9 Tables

TableWhat it stores
btDailyLogsWeight, sleep, daily checklist
btWorkoutsIndividual workouts (type, date, duration)
btExercisesSets within a workout (exercise, weight, reps)
btProgressMonthly body measurements
btIngredientsIngredient database with macros
btRecipesRecipes made from ingredients
btFavoriteMealsFavorite meals (quick add)

Everything indexed for fast queries — by_date, by_workout, by_user. Convex indexes work declaratively in the schema file.

Training Plan in Code

My training plan lives as a TypeScript file. 3-day split with A/B variants:

  • Upper A (Push focus) — bench press, OHP, cable fly...
  • Upper B (Pull focus) — barbell row, pull-ups, face pulls...
  • Lower A (Quad focus) — squat, leg press, leg extension...
  • Lower B (Hip focus) — deadlift, hip thrust, leg curl...
  • Legs + Shoulders — front squat, lateral raises, calf raises...

The app rotates automatically. No thinking about what to train today.

What This Project Taught Me

1. Your own app forces you to log. When I tracked in MyFitnessPal, I sometimes skipped it. But I open my own app because I built it. Ego is a powerful motivator. 2. Convex is ideal for this type of project. Realtime data, TypeScript, zero config. If I did this in Supabase, I'd have 2x more code. 3. Building tools for personal projects is a legit way to learn. I never planned to sell this app. But I learned Convex, realtime subscriptions and schema design on a real use case.
121 kg to 95 kg is a marathon, not a sprint. But having data at hand makes that marathon a lot more bearable.
#next.js#convex#typescript#fitness#case-study

Interested in the article?

Let's discuss what this kind of automation can do in your company.

Free consultation