How to make a recipe site that scales well
Written by Dag Stuan
Recently, Dag Stuan, engineer at Bekk, shared a cool recipe project in the community with nifty use of Portable Text. We asked him to write about his process to make "Bak & del". The name Bak&del is a playful Norwegian pun that combines "bake" (to bake) and "del" (to share), perfectly capturing the spirit of a baking community. As a cheeky bonus, the word bakdel also means “butt” in Norwegian slang—because who says baking can’t have a little spice?
One of the best things about programming is that you can solve everyday problems all by yourself. With my latest hobby project, I’m doing just that. Meet bakdel.no (or go here for English version via Google Translate)!
The problem
In recent years, I’ve started baking bread and other yeast-based goods at home. I have a few recipes I’ve used for a long time, and I also look up recipes online when I want to bake a different type of bread or other baked goods
There are countless recipe sources online, so I rarely find recipes in the same place. Many of the sites are “okay” to use, but I often feel there’s more focus on the person publishing the recipe, than on me, the one actually baking. The recipes are filled with the life stories of the bloggers who write them, and packed with ads or affiliate links to things I can buy. Some sites even use a “bait-and-switch” tactic, placing recipes behind a paywall after a while.
Although the actual recipes often are good, I also find them challenging to use. When I’m on my phone, I have to constantly scroll between the ingredient list and the recipe text, and the instructions are often packed with information I don’t need.
But the biggest issue, and the main reason I created Bakdel, is scaling. When I bake bread, I want to make three loaves at a time because that’s what fits in my oven. The recipes I find are often for one or two loaves, so I have to scale them myself. So when I found myself with an Excel spreadsheet just for baking, I thought to myself “there must be a better way…”.
How could it be better?
What I wanted, was a place where I could:
- Store recipes I like in an easy-to-read, well-organized format. Without ads or unnecessary information.
- Avoid scrolling back and forth between the ingredient list and the recipe instructions while baking.
- Scale recipes so I can bake three loaves instead of two, or make waffles even if I only have two eggs left in the fridge on a Sunday.
The idea
At Bekk Trondheim, we’ve had a few workshop evenings this fall where we learned to use Sanity and completed the Sanity Developer Certification. Sanity is a content operating system that allows me to define content in the form of “documents” or “objects.” Unlike a traditional CMS, content in Sanity isn’t tied to a presentation layer; it’s more akin to a traditional SQL database or other low-level content abstractions.
What sets Sanity apart from other headless CMS solutions is the ability to customize the editor experience. When setting up a project, you’re responsible for configuring Sanity Studio, the user interface for editing content. The studio is very customizable, which makes it relatively easy to tailor an editor experience to the content being created.
When I got familiar with how to set up Sanity, I had an idea; what if I could model recipes and ingredients as content in Sanity, and create references to ingredients within a recipe’s instructions? That way, I could create a content model that made it easy to add recipes and link to the ingredients within the recipe text.
With a bit more logic, I could scale the ingredients dynamically so that adjusted quantities could appear directly in the text. When I showed a small demo to some colleagues, I got the suggestion to add a feature for checking off ingredients as you go, which was also fairly quick to implement. With that, the core product was in place, and most of the time since has been spent on refinement.
Below is a excerpt of the schema code that defines the Portable Text field for Instructions (find all of the code on GitHub):
Internal server error