Gym Workouts
A React workout library app: browse exercises by muscle group, open detailed instructions, and play demo videos. Built on a Vite + TanStack stack with Light / Dark / System theming.
Features
- Exercise library with search and filters (body part, category, difficulty, equipment)
- Exercise detail pages with metadata, step-by-step instructions, and video demos
- Prev / next navigation between exercises
- Weekly custom schedule — assign exercises per day of the week (persisted in
localStorage) - Today / day workout — view today’s plan and preview other days
- Theme switcher: Light, Dark, or System (persisted in
localStorage) - Responsive layout for desktop and mobile
- Graceful empty states when filters match nothing, a day is empty, or a video is missing
Tech stack
- Vite + React + TypeScript
- TanStack Router — type-safe file-based routing (
@tanstack/router-plugin) - TanStack Query — async state / caching
- Axios — HTTP client
- Tailwind CSS — utility-first styling (CSS variables for light/dark)
- shadcn/ui + Lucide React — accessible UI primitives
- ESLint + Prettier + Husky + lint-staged
Getting started
npm install
cp .env.example .env
npm run dev
Open the URL printed in the terminal (usually http://localhost:5173).
Scripts
| Script | Description |
|---|---|
npm run dev | Start the Vite dev server |
npm run build | Type-check and build for production |
npm run preview | Preview the production build |
npm run lint | Run ESLint |
npm run lint:fix | Run ESLint and auto-fix |
npm run format | Format with Prettier |
npm run format:check | Check Prettier formatting |
npm run typecheck | Run TypeScript without emitting |
Routing
Routing uses TanStack Router file-based routes via @tanstack/router-plugin. Route modules live in src/routes/; the plugin generates src/routeTree.gen.ts (do not edit by hand).
| File | Path | Description |
|---|---|---|
routes/__root.tsx | (layout) | App providers, shell, and not-found UI |
routes/index.tsx | / | Exercise library (search + filters) |
routes/today.tsx | /today | Today / day-wise workout view |
routes/schedule.tsx | /schedule | Weekly schedule editor |
routes/exercises.$exerciseId.tsx | /exercises/$exerciseId | Exercise detail, video, and instructions |
The router instance is created in src/router.tsx.
Weekly schedule
Custom schedules are stored under gym-workout-schedule as a versioned payload. Each day keeps workout-group refs (for labels) plus concrete exercise IDs from gym-workouts.json.
Default plan (first visit or after Reset Schedule):
| Day | Workout groups | Focus |
|---|---|---|
| Monday | 4 | Back |
| Tuesday | 7, 8 | Biceps + Abs |
| Wednesday | 2, 3 | Chest + Tricep |
| Thursday | 6 | Shoulder |
| Friday | 1 | Legs |
| Saturday | 5 | Cardio |
| Sunday | — | Rest |
Workout groups resolve to exercise IDs via matchers in src/features/schedule/workout-groups.ts (no duplicated exercise objects). The library has no dedicated biceps/abs primaries, so those groups use the closest tags available in the JSON.
Reference plan file: src/assets/gym/default-weekly-schedule.json.
Changes save immediately. Reset Schedule (on My Schedule) restores the default plan after confirmation and does not clear theme preference.
Workout data & videos
- Exercise metadata lives in
src/assets/gym/gym-workouts.json(50 exercises, IDs0051–0100). - Demo videos live in
src/assets/gym/workouts/as{id}.mp4. - Videos are resolved at build time via Vite
import.meta.globand matched by exercise ID — the JSON is not mutated for video paths. - If an exercise has no matching file, the detail page shows a “No demo video” state.
Fields available per exercise: id, name, bodyPart, equipment, target, secondaryMuscles, instructions, description, difficulty, category.
Theme
Theme preference is stored under the workout-app-theme key (light | dark | system). System mode follows prefers-color-scheme. An inline script in index.html applies the class before React loads to avoid a flash of the wrong theme. Dark mode uses the existing .dark CSS variables in src/styles/index.css.
Folder structure
src/
├── assets/gym/ # Workout JSON + demo videos
├── components/ # Shared UI (theme switcher, shadcn primitives)
├── config/ # App configuration (env, etc.)
├── features/
│ ├── schedule/ # Weekly schedule storage, UI, pages
│ └── workouts/ # Exercise library data, components, pages
├── hooks/ # Shared hooks (theme context helpers)
├── layouts/ # App shell
├── lib/ # Axios, query client, utils
├── providers/ # Theme + schedule + React Query providers
├── router.tsx # createRouter + type registration
├── routeTree.gen.ts # Auto-generated route tree (plugin)
├── routes/ # File-based TanStack Router routes
└── styles/ # Global styles and design tokens
Path alias
@/* resolves to src/*.
Environment
Copy .env.example to .env and adjust values as needed:
cp .env.example .env
| Variable | Description |
|---|---|
VITE_APP_NAME | Display name in the app header |
VITE_API_BASE_URL | API base URL for Axios (default /api) |