Waqas Bajwa

ReciPen, a recipe book for my family

I built ReciPen so families could keep their recipes in one place and share them. I was learning Vue 3 at the time, and I wanted to publish a complete app that real people would use.

Type
Personal project, built in my own time
Frontend
Vue 3, Vue Router, Pinia, Vite
Backend
Cloud Firestore, Firebase Authentication with Google sign-in
Hosting
Render
Code
devbajwa/recipe-book on GitHub (opens in a new tab)
Status
Live. Open ReciPen (opens in a new tab)

Why I built it

Family recipes usually live in notebooks, chat messages and people's heads. I wanted one place where my wife and my mum could write them down, find them again, and see which ones the family likes.

It was also how I learned Vue 3. Instead of following another tutorial, I built something my own family would use and put it online.

What it does

The home page lists every recipe, newest first. Each card shows the category, a short description and how many people liked it. A search box narrows the list by recipe name as you type.

The ReciPen recipe list: a search box above a grid of six recipe cards, including Walnut Cake, Chicken Karahi and Masala Tea, each with a category label, a save icon and a like count.
The recipe list on desktop. I blurred the names of the people who added each recipe.

Opening a recipe shows a summary first: category, number of ingredients, prep and cook time, how many people it serves, sugar and spice level, the occasion and the date it was added. The ingredients with their quantities and the method steps sit below.

The Chicken Karahi recipe page: a summary panel with prep time 25 minutes, cook time 20 minutes, serves 5 people and spice level high, then a short description, a list of 14 ingredients and the method.
A recipe page.
The recipe list on a phone: the Explore Recipes heading, a full-width search box and recipe cards stacked in one column.
The same list on a 390 px wide phone screen.

Signing in

Anyone can browse and read recipes. Everything that changes data needs a Google sign-in, which Firebase Authentication handles.

  • Signed-in people can add a recipe, like a recipe and save it to their collection. Signed-out visitors who try to like or save see a message asking them to sign in.
  • Favourites lists the recipes you saved, and My recipes lists the ones you added.
  • Only the person who added a recipe sees the Edit and Delete links on it. Delete asks for confirmation first.

Adding a recipe

The form asks for a name, a description, the ingredients and the method steps. Ingredients and steps are repeating rows: each ingredient has a name and a quantity, and you add rows as you need them.

The rest of the form fills the summary on the recipe page: a category (curry, rice, snack, dessert, salad or beverage), sugar and spice level, prep and cook time, serving size, occasion and the chef's name. If something is missing, a message asks you to check the form.

How it is built

  1. Vue 3 single-file components using the Composition API, built with Vite.
  2. Vue Router handles the pages. A route guard asks Firebase Authentication for the current user before it opens My recipes, Favourites or the edit page, and sends signed-out visitors back to the home page.
  3. A Pinia store holds the signed-in user, so the header, the cards and the forms read the same state.
  4. Two service files wrap every Firestore call, so components never query the database directly.
  5. Firestore has two collections. recipes holds each recipe with its like and view counts. userInteractions holds one document per person, with the IDs of the recipes they liked and saved.
  6. Before a like is counted, the app checks that person's userInteractions document, so each person can like a recipe once. The count goes up with Firestore's increment, so two likes at the same moment both count.
  7. Short toast messages confirm sign-in, likes, saves, updates and deletes.

How it ships

  • I built each feature on its own branch, such as Firebase setup, auth, routing, search, edit and delete, and favourites and likes, and merged them into main through pull requests.
  • Render builds the app from the GitHub repository with Vite and serves the result.
  • Google Analytics came later, in its own pull request.