Favorites Feature
What I Built:
Added a favorites tab where users can save recipes they like. There’s a heart icon on every recipe card and on the recipe detail page. Tapping it saves the recipe locally. The favorites tab shows all saved recipes with a count at the top.
How It Works
Favorites are stored in WatermelonDB, same as the pantry ingredients. I added a new favorites table to the schema with columns for recipe ID, title, ingredients, directions, cook time, difficulty, and equipment. Since WatermelonDB doesn’t support array columns, I store arrays as JSON strings and parse them when reading.
The heart icon is its own component called FavoriteButton. It checks the database on mount to see if the recipe is already saved, and fills the heart if it is. Tapping it adds or removes the recipe.
The favorites tab uses withObservables to reactively display saved recipes. When you favorite or unfavorite something, the list updates right away without needing to refresh.
Problems I Ran Into
Heart not filled on recipe detail page - I was passing the wrong object to FavoriteButton. The recipe from useRecipeDetail doesn’t have an id field, so the favorite check always failed. Fixed by passing the manually built recipe object that includes the ID.
“0 min” instead of “Unknown” - WatermelonDB returns 0 for empty number columns instead of null. The ?? operator doesn’t catch 0 because it only handles null and undefined. Switched to || which treats 0 as falsy.
“Uses your: “ with nothing after it - The favorites list was passing an empty ingredients array to RecipeCard. Fixed by also observing the ingredients table in FavoritesList so it knows what’s in the pantry.
Heart positioning - The heart icon would sometimes go off the card depending on title length. Fixed by using absolute positioning in the top right corner with pr-12 on the title so they don’t overlap.
Tests breaking after merge - The ingUsed.test.ts test started failing because RecipeCard now imports FavoriteButton, which imports the database, which tries to start SQLiteAdapter. SQLiteAdapter is a native module that doesn’t exist in Jest. Fixed by mocking the database module in the test file.
Schema migration - Had to bump the schema version from 2 to 3 and write a migration file so existing installs don’t crash. Also had to clear app data on my phone during development because the old database didn’t have the favorites table.