A modern web application for studying US Amateur Radio license exams. Open source, community-driven, born in North Carolina.
Live App: app.openhamprep.com
- Practice Tests - Simulated exam experience with optional timer
- Random Practice - Study questions with instant feedback
- Study by Topics - Focus on specific subelements with learning resources
- Weak Questions - Review questions you've missed
- Glossary & Flashcards - Learn key terms with spaced repetition
- Progress Tracking - Dashboard with test readiness, streaks, and goals
- Bookmarks - Save questions with personal notes
Supports all three license classes: Technician, General, and Extra.
Prerequisites: Node.js 20+, Docker Desktop
git clone https://github.com/sonyccd/openhamprep.git
cd openhamprep
npm install
npm run dev:full- App: http://localhost:8080
- Database GUI: http://localhost:54323
See LOCAL_DEVELOPMENT.md for details.
Frontend: React 18, TypeScript, Vite, Tailwind CSS, shadcn/ui, TanStack Query, Framer Motion
Backend: Supabase (PostgreSQL, Auth, Edge Functions, Realtime)
Tooling: Vitest, ESLint, Pendo (analytics), Sentry (error tracking)
- Global License Context - App-wide filter (Technician/General/Extra) via
useAppNavigation - Authentication - Supabase Auth via
useAuthcontext - Data Fetching - TanStack Query with custom hooks in
src/hooks/ - Progress Tracking - User attempts saved per-question for analytics
The app wraps components in this provider order (see App.tsx):
- ThemeProvider (next-themes)
- QueryClientProvider (TanStack Query)
- AuthProvider (Supabase auth)
- AppNavigationProvider (license filter)
src/
├── components/ # React components
│ ├── ui/ # Base shadcn components
│ ├── admin/ # Admin-only components
│ └── *.tsx # Feature components
├── hooks/ # Custom React hooks
├── pages/ # Route page components
├── lib/ # Utilities
├── integrations/ # Supabase client (auto-generated)
└── types/ # TypeScript types
Important: Never use direct colors. Always use semantic tokens:
// Correct - works in light/dark mode
<div className="bg-background text-foreground">
// Wrong - breaks theme switching
<div className="bg-white text-black">Available tokens: background, foreground, card, primary, secondary, muted, accent, destructive, success, border
See src/index.css for all token definitions.
npm run dev # Start dev server
npm run dev:full # Start Supabase + dev server
npm run supabase:stop # Stop Supabase
npm run supabase:reset # Reset database
npm test # Run tests
npm run build # Production buildUses local Supabase via Docker - no hosted account needed:
npm run dev:full # Start Supabase + dev serverFor production or if you have Supabase project access:
cp .env.example .env.local
# Edit .env.local with your credentials
npm run dev- Local Development Guide - Setup with local Supabase
- Testing Guide - Vitest testing patterns
- Contributing Guidelines - Code style and PR process
- Deployment Guide - GitHub Pages + Vercel setup
- Claude Code Instructions - AI assistant guidelines
- OAuth Setup - Social login configuration
- Fork the repo
- Run locally with
npm install && npm run dev:full - Make your changes
- Submit a pull request
See CONTRIBUTING.md for guidelines.
© Brad Bazemore