A Personal Vocab Journal#
What I Built#
Vocabography is a mini web app that acts as my personal vocab journal. The idea behind it is simple, instead of a regular app giving me a word every single day to remember, I bring in the words I actually encounter in real life (let’s say from a book I’m reading, a conversation, or a show) and add them myself. Each word gets an auto-fetched dictionary definition, then I add my own example sentence using the word. After some days, the app quizzes me by blanking out the word in my own sentence, rather than a default example. And finally the results are added in the score section where I can track my progress.
Live Site#
How it works#
- Catch a word. Just type in the word.
- Auto-lookup. Vocabography automatically fetches its definition and part of speech from the Free Dictionary API (no API key needed).
- Make it yours. Write your own example sentence using the word.
- Store it. Everything gets saved in the journal as well as in the firestore database (so it’s accessible whether you are on a phone or laptop).
- Streak. Click the 🔥 in the nav bar to see a GitHub-style heatmap of every day you’ve added a word, plus your current and longest streaks.
- Search your collection. The Collection tab lists every word you’ve ever added, most recent first, styled like index cards pulled from a catalog drawer.
- Review & score. Vocabography quizzes you with your own sentence, blanking out the word you have to recall. Get it right and the interval grows (3 → 7 → 21 days) and you earn points. If you get it wrong it resets back to 3 days. The quiz pops up automatically the moment a word becomes due, and the 🏆 tab tracks your total points, correct/missed counts, and accuracy.
- Themes. You can easily toggle between a dark ink-and-parchment theme and a warm cream light theme from the nav bar.
Tech stack#
- Styled with Tailwind CSS v4
- Built with Vite
- Written in React + TypeScript
- Stored in Firebase Firestore (a cloud database), where word entries live in a
wordscollection, while streak and quiz stats live together in a single document calledmeta - Theme preference is stored separately in
localStorage, since it’s a device’s local setting, not journal content
No login here, for now it’s just for me. Rules are open on purpose (you can check firestore.rules).
Complete Code#
github.com/waleeja07-wk/vocabography
Project structure#
├── firebase.json # Firebase CLI config (points hosting at dist/, deploys firestore.rules)
├── firestore.rules # Firestore security rules (open access, single-user app)
├── vite.config.ts # Vite build configuration
├── tsconfig.json # TypeScript compiler settings
├── package.json # Dependencies and npm scripts
├── .env.local # Firebase config keys (It is gitignored)
src/
├── types/word.ts # LoggedWord, ReviewState, StreakData, QuizStats, AppSettings
├── lib/ # Pure business logic, no React
│ ├── firebase.ts # Firebase app + Firestore instance initialization
│ ├── storage.ts # Firestore read/write for words, streak, and quiz stats
│ ├── settings.ts # localStorage read/write for theme
│ ├── dictionaryApi.ts # Free Dictionary API integration, with retry handling
│ ├── streak.ts # Streak calculation
│ ├── spacedRepetition.ts # Quiz interval scheduling and scoring
│ └── dateUtils.ts # Shared date helpers
├── hooks/ # Thin React layer over lib/
│ ├── useWords.ts # Owns word data, loading/error state, and all mutations
│ ├── useStreak.ts
│ ├── useReviewQueue.ts
│ └── useSettings.ts
└── components/
├── AddWordForm.tsx # Search for a word, then write your sentence
├── WordCard.tsx # Word added, catalog-card style
├── HistoryList.tsx # All the words currently added, most recent first
├── NavBar.tsx # Home / Collection / Streak / Quiz + theme toggle
├── ActivityHeatmap.tsx # GitHub-style daily activity map
├── QuizScorePanel.tsx # Total points, correct vs incorrect
└── ReviewQuiz.tsx # Fill in the blank review, auto-popup on due dayRun Locally#
- Install dependencies:
npm install - Create a Firebase project at console.firebase.google.com, enable Firestore, and register a web app to get your config values.
- Create a
.env.localfile in the project root:VITE_FIREBASE_API_KEY=... VITE_FIREBASE_AUTH_DOMAIN=... VITE_FIREBASE_PROJECT_ID=... VITE_FIREBASE_STORAGE_BUCKET=... VITE_FIREBASE_MESSAGING_SENDER_ID=... VITE_FIREBASE_APP_ID=... - Deploy the Firestore rules in
firestore.rules:firebase deploy --only firestore:rules - Run the dev server:
npm run dev
Deployment#
Deployed with Firebase Hosting:
npm run build
firebase deploy --only hosting6-hour is just a myth#
This internship task was estimated to be completed in 6 hours. But it took almost 10x that for writing, rebuilding, debugging, and actually understanding what I was building, But yeah, it added up. Every effort was worth it..
Improvements planned for future#
Things I’d love to add if I keep working on this:
- Add Firebase Authentication so multiple people can each keep their own
private word collection, with security rules enforcing per-user data
isolation (
request.auth.uid == resource.data.userId). - Show upcoming review dates in the history view.
- Export your journal as a shareable list.

