This site runs best with JavaScript enabled.
Project LIVESource code

Habit Tracker App

Habit Tracker is my final project for Harvard's CS50 course. It's built as MVP (minimal viable product) and it's core functionality is to track user's habits.

Video presentation

Already implemented features

  • Adding, editing and deleting habits
  • Marking habits as completed, failed or skipped
  • Visualization of user performance
  • Customizing app's theme
  • Changing the language: EN|ES|PL


  • React - my favorite library for building interactive user interfaces.
  • React Query — fetching, caching and updating asynchronous state. Also, I could significantly improve performance by implementing optimistic updates.
  • React Router — routing but also it was an opportunity to check out the newest React Router v6 that is still in beta.
  • React Hook Form — forms and validation.
  • Material UI — my favourite user interface library — it's easy to use and implement and it's just so much easier to make the app mobile friendly.
  • Firebase - Authentication, Realtime Database and Hosting.


I learned a lot while building the project and for sure I'm going to learn a lot more while maintaining it. That's why I want to keep track of the challenges that I've had along the way so that I can reference them in the future.

Database and data structure

How should I store habit's completion state for each day? Should each habit have an array with the dates when it was performed or should I store dates and each date would keep track of the habits that where performed on that day?

I tried to structure the data so that it is saved and retrieved as easily as possible. To do so I've been following Firebase guidelines and in the end came up with the following data structure:

2 "habits": {
3 "user-one": {
4 "habit-one": {
5 "name": "Reading",
6 "description": "Read a book for at least 30 min in the morning",
7 "frequency": [0, 1, 2, 3, 4]
8 }
9 }
10 },
11 "checkmarks": {
12 "user-one": {
13 "checkmark-id": {
14 "habitId": "habit-one",
15 "date": "2020-11-11",
16 "value": "completed"
17 }
18 }
19 },
20 "users": {
21 "user-one": {
22 "locale": {
23 "code": "en-GB"
24 },
25 "theme": {
26 "primaryColor": "blue",
27 "secondaryColor": "red",
28 "dark": true
29 },
30 "performanceGoal": 80
31 }
32 }

Authentication Layer

For quite some time I was using Private and Public routes to prevent an authenticated user from accessing the parts of the app available only for logged in user. It was fine but I wanted to use a different layout for authenticated users (additional sidebar on the left).

I found the perfect solution in a blog post by Kent C. Dodds:

1function App() {
2 const user = useUser()
3 return user ? <AuthenticatedApp /> : <UnauthenticatedApp />

Localization and language

I've never before implemented this in an app and I really wanted to give it a try. My main goal was to give the user an option to change their locale and language. Although this goal was achieved, the solution is far from ideal. First of all, I think that it would be better to split these two layers. For example in YouTube one can open settings and change either Language or Location.

Back to projects

Send message


by Maciek Sitkowski