A polished, standalone web app for practicing the NeetCode 150 — without ever repeating a problem until you've seen them all.
🌐 Live demo: https://aadityajo.github.io/neetcode-cycle-randomizer/
# 1. Install dependencies
npm install
# 2. Start the dev server
npm run dev
# 3. Open http://localhost:5173/neetcode-cycle-randomizer/npm run deployThis builds the app and pushes to the gh-pages branch. Make sure your repo has GitHub Pages enabled pointing to that branch.
src/
├── data/
│ └── problems.ts ← 🔑 NeetCode 150 dataset (swap here!)
├── types/
│ └── index.ts ← All TypeScript interfaces
├── lib/
│ ├── storage.ts ← LocalStorage read/write helpers
│ ├── cycle.ts ← Core random-cycle algorithm
│ ├── filters.ts ← Filter/sort utilities
│ └── progress.ts ← Progress calculation
├── hooks/
│ └── useAppState.ts ← Central state + all actions
├── components/
│ ├── Header.tsx ← Sticky header + dark mode toggle
│ ├── StatsRow.tsx ← Four stat cards
│ ├── CurrentProblemPanel.tsx ← Problem card + pick button
│ ├── ProblemList.tsx ← Filterable/sortable table
│ ├── ResetControls.tsx ← Reset, export, import, settings
│ ├── ProgressBar.tsx ← Reusable progress bar
│ └── Toast.tsx ← Toast notification system
├── App.tsx ← Root layout
└── main.tsx ← React entry point
pickNext():
1. Build pool = allProblems − seenThisCycle
(optionally filter out completedProblemIds if excludeCompleted = true)
2. If pool is empty → mark cycleComplete = true, show celebration
3. Otherwise → pick pool[random index]
4. Add chosen.id to seenThisCycle
5. Set currentProblemId = chosen.id
6. Persist to localStorage
The key invariant is: once a problem enters seenThisCycle, it cannot be picked again until a new cycle begins.
| Field | Purpose |
|---|---|
allProblems |
Full problem list (from data/problems.ts) |
seenThisCycle |
Set of IDs seen in the active cycle |
completedProblemIds |
IDs marked ✅ (persists across cycles) |
needsReviewProblemIds |
IDs marked 🔖 |
skippedProblemIds |
IDs marked ⏭️ |
currentCycleNumber |
1-indexed cycle count |
currentProblemId |
Currently displayed problem |
cycleComplete |
True after all problems are seen |
excludeCompleted |
When true, completed problems skip future cycles |
Open src/data/problems.ts — there is a single PROBLEMS array at the top of the file. Replace or extend it with your own dataset. Each entry must match:
interface Problem {
id: string // unique, any string
title: string
category: string // e.g. "Arrays & Hashing"
difficulty: 'Easy' | 'Medium' | 'Hard'
url: string // LeetCode or NeetCode URL
}The file already contains all 150 NeetCode 150 problems seeded and ready to use. The categories match NeetCode's official groupings.
- 🎲 Random non-repeating cycle — guaranteed no repeats until all seen
- 📊 Progress tracking — cycle number, seen/total, completed overall
- ✅ Problem actions — mark completed, needs review, skipped
- 🔍 Filterable list — search, category, difficulty, status
- 🎯 Surprise mode — pick from a specific category only
- 📦 Export / Import — save progress as JSON
- 🎉 Confetti on cycle completion
- 💾 LocalStorage persistence — refresh-proof
- 🌙 Dark mode (default) with toggle
- 📱 Responsive layout
- React 18 + TypeScript
- Vite — dev server and bundler
- Tailwind CSS — utility-first styling
- canvas-confetti — cycle completion celebration
- lucide-react — icons
- LocalStorage — all persistence, no backend