Chapter 8: Streaks & Stats
Habits need feedback loops. A streak counter and stats give your users a reason to come back tomorrow.
Your app works. Users can create entries, see them in a feed, and manage their profile. But there is nothing pulling them back each day. No sense of progress, no momentum. In this chapter, we add the two most effective habit-building tools in any app: a streak counter that rewards consistency and stat cards that show progress at a glance.
What You'll Build
- A streak calculator that tracks consecutive days of journaling
- A fire icon in the header that animates and fills based on the current streak
- A pop animation that celebrates when the streak increments
- A calendar modal showing which days the user submitted entries
- Three profile stat cards: Current Streak, Best Streak, and Entries Created
What You'll Learn
- Streak calculation logic and date arithmetic
- How to build animated SVG icons in React
- Modal patterns with calendar views
- Storing derived stats in Firebase against a user document
- Celebratory micro-interactions that reinforce habits
How Streak Logic Works
Before we start building, it is worth understanding what "streak" means in practice. A streak counts the number of consecutive calendar days a user has submitted at least one entry. Here are the rules:
- Today counts. If the user journaled today, that day is part of the streak.
- Yesterday still counts. If the user has not journaled today but did yesterday, the streak is still alive. They might journal later today -- we should not punish them for opening the app in the morning before writing.
- Multiple entries on the same day count as one. Two entries on Tuesday is still just one streak day.
- Missing a day resets the streak to zero. If the last entry was two or more days ago, the streak breaks.
- Best streak is tracked separately. Even when the current streak resets, the all-time best is preserved.
This "yesterday grace period" is the same pattern used by Duolingo, Wordle, and most habit-tracking apps. It keeps the streak motivating without being punishing.
Step 1: Build the Streak Calculator
First, we need the core logic that takes an array of entry dates and computes the current streak, best streak, and total entries. This is pure date math -- no UI yet.
$ claude "Create a streak calculator and visual in the app. Below the header lets add a da..."Claude Code will build several things from this single prompt. The streak calculator will handle all the date math -- deduplicating entries that fall on the same day, counting backwards from today to find consecutive days, and tracking the longest streak ever achieved. The fire icon will be drawn as an SVG that fills with color based on the current streak value. When the streak is zero, the fire appears as an outline. As the streak grows, the flame fills in with warm amber and orange tones. Tapping the fire opens a calendar modal showing the current month with small circles on each date -- filled circles with a checkmark for days the user submitted an entry, empty circles for days they did not.
Step 2: Add the Streak to the Header
After Step 1, you should already see the streak counter in your header. Take a moment to verify it is positioned correctly -- it should sit on the right side of the header, showing the fire icon alongside the current streak number. If the positioning is off or the fire icon does not appear, describe what you see to Claude Code and it will adjust.
Start your dev server if it is not already running:
npm run dev
Create a new entry and watch the streak counter. It should show "1" next to the fire icon. The fire should have some color fill indicating an active streak. Tap the fire icon to open the calendar modal and confirm today's date shows a filled circle with a checkmark.
Step 3: Add the Pop Animation
Now let's add the satisfying moment -- when the user submits an entry and their streak increments, a celebratory pop animation plays. This is a micro-interaction that makes the app feel alive and rewards the user for consistency.
$ claude "Add a short celebratory modal with a pop-in (or scale/fade) animation. It should..."The pop animation should feel quick and satisfying -- not slow or blocking. It appears briefly after the entry saves, shows the fire icon with an animated flame effect, and the streak number counts up from the old value to the new one. After a moment it dismisses automatically or when the user taps anywhere. This is the same pattern Duolingo uses when you complete a lesson and your streak increments.
Step 4: Add Profile Stats
The Profile page needs more than just an email and a logout button. Let's add stat cards that give the user a dashboard view of their journaling habit.
$ claude "Show three stat cards in a grid in the profile: Current Streak (Flame icon), Bes..."The three stat cards should appear in a row at the top of the Profile page. Each card has an icon, a prominent number, and a short label:
- Current Streak with a flame icon -- shows how many consecutive days the user has journaled
- Best Streak with a trophy icon -- the all-time longest streak, persisted in Firebase so it survives even if the current streak resets
- Entries Created with a calendar icon -- total number of entries the user has ever created
Claude Code may update your Firestore rules or add a user document to store the best streak persistently. The current streak can always be recalculated from entry dates, but the best streak needs to be stored because old entries might be deleted.
Step 5: Test Everything
Start your dev server if it is not already running:
npm run dev
Work through this testing checklist:
- Streak counter in header -- verify the fire icon and streak number appear on the right side of the header
- Create a new entry -- the streak should show "1" and the fire icon should fill with color
- Pop animation -- after submitting, a brief celebration animation should play showing the streak increment
- Tap the fire icon -- the calendar modal should open showing the current month with today marked as complete
- Profile stats -- navigate to Profile and verify all three stat cards display correct values
- Multiple entries same day -- create another entry. The streak should stay at 1 (not increment to 2) since both entries are on the same calendar day
- Fire icon state -- with an active streak, the fire should appear filled and animated. If you could test with no streak (fresh account), it should appear as an unfilled outline
Testing multi-day streaks is tricky since you cannot easily fast-forward time. The simplest approach is to trust the logic and verify the single-day case works. If you want to be thorough, you can temporarily modify the streak calculator to use a hardcoded array of dates spanning multiple days, verify the count is correct, then revert the change.
Step 6: Commit Your Progress
Once everything is working, save your progress:
git add .
git commit -m "Add streak counter with fire animation, calendar modal, pop celebration, and profile stats"
git push
Checkpoint
Your app should now:
- A fire icon in the header shows the current streak count
- The fire animates and fills when the streak is active
- Tapping the fire opens a calendar modal showing submission history
- A pop animation celebrates when the streak increments
- The Profile page shows three stat cards: Current Streak, Best Streak, and Entries Created
- Streak logic correctly handles same-day entries, the yesterday grace period, and streak resets
- Best streak is persisted in Firebase so it survives streak resets
What's Next
You have finished the core Part 4 curriculum. There is an optional bonus chapter that adds a living tree home screen, renames the list tab to Feed, and moves entry creation into a slide-up sheet with a floating add button — great extra polish before you go mobile.
Follow @parvsondhi for build threads, tips, and updates on this tutorial.
Next: Chapter 9 (Bonus) — Having Fun →
If you prefer to skip straight to native iOS: