Building a vintage recipe rolodex with Python, Pelican, and Markdown. | josh iza.ac
I enjoy cooking. Not only that, but I enjoy complicated cooking — which can sometimes be a bit of a curse.
A simple weeknight meal? Not interested. A week-long project to nail the ultimate Xiao Long Bao, Croissants, or homemade ramen?<br>Sign me up! I become somewhat obsessive, reading cooking blogs,<br>cookbooks, and YouTube videos to figure out not just the baseline techniques and ingredients, but<br>the endless variations between them. And as I refine a recipe and my method, I make sure to write<br>down what worked (and what didn’t!).
In the beginning, my method was to use Microsoft Publisher and generate a PDF (to be fair, it was<br>2004). As my physics career took off and I became surrounded by open-source tech and tooling, this<br>transitioned to PDFs generated from simple Markdown files stored in Dropbox.
But this setup had its flaws. PDFs don’t render well on mobile phones, and Dropbox isn’t exactly the<br>ideal platform for seamlessly maintaining, updating, and sharing a repository of recipes. At the<br>time, I had already created this website, which was built on Pelican — a static-site generator for Python built on Markdown files. Since I<br>already had an affinity for Markdown, I figured there must surely be a library that allows me to<br>store and version my recipes using Markdown and Git, yet still render them for easy browsing and searching.
From what I could find… not really.
The two most promising options were Chowdown (however, I wanted to avoid a Jekyll-based solution) and Jeff Thompson’s Recipe Website (I loved the style, but wanted a static site generator). There were also a couple of cloud services and phone apps that store recipes in Markdown formats (such as Tandoor and Grocery), but I didn’t want to be tied down to one platform or depend on paid tiers just to get the most out of my Markdown files.
So, armed with a researcher’s knowledge of Jinja, Python, JavaScript, and CSS, I thought: why not build it myself?
The Pelican Cookbook plugin
I started this project with a few core goals:
Plain text format : Recipes need to be stored as plain text and remain readable .
Can be rendered as HTML : They should render nicely as static HTML with a minimalist theme,<br>allowing readers to get right to the point: the ingredients list and the steps.
Cross-references and components: Recipes need to be able to cross-reference each other, and<br>even embed each other as components (to avoid writing out the same sub-recipes over and over!).
Timelines : It needs to be easy to extract information about complex recipes. If I can only<br>start cooking at 4:45 PM, what time will I be eating?
Search: Both recipes and individual ingredients need to be searchable .
Using the following Markdown format for a recipe keeps everything highly readable, with perhaps the<br>exception of some website-specific YAML frontmatter at the top:
Title: Lime curd<br>Date: 2025-12-24<br>Category: Sweets<br>Slug: lime-curd<br>Time: 30 minutes<br>Servings: 750ml<br>Image: /images/recipes/lime-curd.jpg
## Ingredients
* 115g unsalted butter (cut into 1cm cubes)<br>* 180ml freshly-squeezed lime juice (about 5-6 limes)<br>* 140g sugar<br>* zest of two limes, unsprayed (see Note)<br>* pinch of salt<br>* 3 large egg yolks<br>* 3 large eggs
## Steps
1. In a glass bowl over a saucepan filled with simmering water, warm the butter, lime juice, sugar,<br>zest, and salt.
1. In a separate bowl, whisk together the eggs and the yolks.
1. When the butter has melted and the mixture is warm, gradually pour some of the warm lime juice<br>mixture into the eggs, whisking constantly. Scrape the warmed eggs back into the double boiler and<br>cook the mixture over low heat.
1. Stir the mixture constantly over low heat, using the whisk, until the filling thickens and begins<br>to resemble soft jelly. Do not let it boil. When done, strain the curd through a fine-mesh sieve.
You can see the raw Markdown file on GitHub, and the<br>resulting rendered page. This easily hits points (1) and (2).
To solve point (3), I added the ability to use [[Recipe title]] to insert an automatic<br>cross-reference to other recipes. Going one step further, you can use [[Component: recipe title]]<br>within an ingredients list or method in order to insert another recipe’s instructions directly<br>into the current one!
Title: Lime meringue tart<br>Date: 2025-12-19<br>Category: Pastry<br>Slug: lime-meringue-tart<br>Time: 1 hour<br>Servings: 1 9-inch (22cm) tart<br>Image: /images/recipes/lime-meringue-tart.jpg<br>Tags: dessert
## Ingredients
* 1 blind-baked [[Pâte Sablée]] tart shell.<br>[[Component: lime curd]]<br>[[Component: swiss meringue]]
## Instructions
1. Preheat the oven to 180ºC.
[[Component: lime curd]]
1. Pour the curd into the pre-baked tart shell, and bake for 10 minutes.
[[Component: swiss meringue]]
What about cooking timelines?
Simply include the Timeline metadata:
Timeline: Mix Ingredients | 15m | Active; First proof | 60m | Wait; Second proof | 45m | Wait; Shape | 15m |...