Tony Herrera25 downloadsAdd recipes by answering step-by-step questions, save each one as a note, and keep an overview note with a table of every recipe. Link recipes that go well together to plan meals.
Add recipes to your Obsidian vault by answering a few questions, one at a time. Each recipe becomes a note with its ingredients, steps, and an ingredient checklist, and appears in an overview note (a "recipe MOC") with a table of every recipe. Link recipes that go well together to put a meal together.
Built for both typing in a family recipe and pasting one from a website: pasted lists are tidied up for you.
1., 1), and Step 1: numbering, extra blank lines, and stray spaces are removed. Instructions are numbered for you.onion: 1 unit or 2 cups rice into rows. Ingredient names you've used before are suggested.45, 1:15, 1h15, or 1 hr 15 min. The total is added up.ingredients → group → type → name: amount, as well as simpler top-level lists. The table is written so that no recipe can drop out of it, whatever a note contains.{{variables}} as the built-in one.From Community plugins: Settings → Community plugins → Browse → search for Recipe Notes → Install → Enable. Its page in the directory: community.obsidian.md/plugins/recipe-notes.
By hand: download main.js, manifest.json, and styles.css from the latest release into <your vault>/.obsidian/plugins/recipe-notes/, then enable Recipe Notes in Settings → Community plugins.
Recipes, with notes in Recipes/All Recipes).Recipes/Recipe MOC.md) is created with its table the first time.| Command | What it does |
|---|---|
| Create recipe note | Opens the step-by-step window (also the chef's hat in the ribbon). |
| Edit pairings | For the open recipe note: change which recipes it goes well with. You see every change, including those in other recipes, before it's saved. |
| Rebuild ingredient checklist | For the open recipe note: rewrite its ingredient checklist from its properties, with every box unticked. Adds a checklist to older notes, after asking. |
| Regenerate recipe table | Rebuild the table in the recipe MOC from the current settings. You confirm first; nothing else in the note changes. |
Edit pairings and Rebuild ingredient checklist are offered only when the open note is in the recipe notes folder.
| # | Question | Saved as |
|---|---|---|
| 1 | Recipe name (required) | The note's file name |
| 2 | Who it's from | From |
| 3 | Link to the recipe | Source URL |
| 4 | Short description | Note text, under Overview |
| 5 | Course (buttons from your course list) and genre | Course, Genre |
| 6 | Servings | Servings (a number, or text such as 6-8) |
| 7 | Prep time and cook time | Prep Time, Cook Time, Total Cooking Time |
| 8 | Ingredients, by group | ingredients |
| 9 | Instructions | A numbered list |
| 10 | Tags | tags |
| 11 | Goes well with | Goes well with |
| 12 | Photo | Photo and a picture at the top of the note |
| 13 | Notes and tips | Note text, under Notes |
Beef Hash 2.md), or Cancel.With the built-in template, a made-up recipe looks like this:
---
tags: ["weeknight"]
Course: ["entree"]
Genre: comfort food
ingredients:
Protein:
type:
ground beef: 1 lb
Carbs:
type:
potatoes: 2 medium
Veggies:
type:
onion: 1 unit
Spices:
type:
smoked paprika: 1 tsp
Other:
type:
Servings: 4
Prep Time: 15 minutes
Cook Time: 25 minutes
Total Cooking Time: 40 minutes
From: Grandma
Source URL: "https://example.com/beef-hash"
Goes well with: ["[[Lemon Green Beans]]"]
Photo: "[[Beef Hash.jpg]]"
created: "2026-11-04 18:30:00"
links: ["[[Recipe MOC]]"]
---
# Overview
![[Beef Hash.jpg|400]]
A quick, cheap weeknight hash.
**From:** [Grandma](https://example.com/beef-hash) · **Servings:** 4 · **Prep:** 15 minutes · **Cook:** 25 minutes · **Total:** 40 minutes
**Goes well with:** `= this["Goes well with"]`
## Ingredients
%% recipe-notes:checklist:start (made from the ingredients properties: "Rebuild ingredient checklist" rewrites this part) %%
### Protein
- [ ] ground beef: 1 lb
### Carbs
- [ ] potatoes: 2 medium
### Veggies
- [ ] onion: 1 unit
### Spices
- [ ] smoked paprika: 1 tsp
%% recipe-notes:checklist:end %%
## Instructions
1. Dice the potatoes and brown them.
2. Add the beef and onion.
3. Season and serve.
## Notes
Keeps in the fridge for three days.
none or N/A; an empty ingredient group is an empty type:.1 hour 15 minutes, which Dataview reads as a length of time.{Recipes folder}/{Recipe MOC name}.md, created the first time you create a recipe or regenerate the table. It's found even if its name differs only in capital letters. If no note has that name but one in the recipes folder looks like a recipe MOC ("MOC" in its name, or a Dataview table that reads your recipe notes), the plugin asks whether to use it instead of creating a second one.
Regenerate recipe table replaces only the part between its two markers. In a MOC you made yourself with exactly one Dataview table, it offers to Replace that table (only that code block changes) or Add at the end. It also adds the class recipe-notes-moc to the note's cssclasses, which makes the note use the full width of its pane so the table fits.
The generated table with the default settings:
%% recipe-notes:start (generated: "Regenerate recipe table" replaces only the part between these markers) %%
```dataview
TABLE WITHOUT ID
file.link AS Recipe,
ldefault(list(ingredients).Protein.type[0], ldefault(list(ingredients).protein.type[0], Protein)) AS "Protein",
ldefault(list(ingredients).Carbs.type[0], ldefault(list(ingredients).carbs.type[0], Carbs)) AS "Carbs",
ldefault(list(ingredients).Veggies.type[0], ldefault(list(ingredients).veggies.type[0], Veggies)) AS "Veggies",
ldefault(list(ingredients).Spices.type[0], ldefault(list(ingredients).spices.type[0], Spices)) AS "Spices",
ldefault(list(ingredients).Other.type[0], ldefault(list(ingredients).other.type[0], Other)) AS "Other",
Course AS Course,
Servings AS Servings,
row["Total Cooking Time"] AS "Total time",
row["Goes well with"] AS "Goes well with"
FROM "Recipes/All Recipes"
SORT file.name ASC
```
%% recipe-notes:end %%
Why the ingredient columns look unusual: Dataview silently leaves out a row when one of its columns fails, and looking up a name inside plain text fails (for example Carbs: rice typed by hand). Wrapping the property in a one-item list (list(ingredients)) makes Dataview skip what fails instead, and [0] takes the value back out, so every recipe stays in the table. Each column also accepts the property name with its first letter in the other case (fats / Fats) and a top-level list of names. The columns follow the Ingredient groups setting; after changing groups, run Regenerate recipe table.
| Setting | Default | |
|---|---|---|
| Recipes folder | Recipes |
Holds the recipe MOC and the two folders below. Can be inside another folder. |
| Recipe notes folder | All Recipes |
Inside the recipes folder. The table lists every note in it, subfolders included. |
| Images folder | Images |
Inside the recipes folder (may be inside the recipe notes folder, e.g. All Recipes/attachments). Downloaded photos go here. |
| Open note after creating it | on | |
| Add pairings to both recipes | on | See Goes well with. |
| Recipe MOC name | Recipe MOC |
|
| Template file | (empty) | Empty = built-in template. Create an editable template saves it as Templates/Recipe note.md and selects it. |
| Ingredient groups | Protein, Carbs, Veggies, Spices, Other | Each has a column name (the table column and the section in the window) and a property name (how it's stored under ingredients). Add, remove, and drag to reorder. |
| Course choices | entree, side, dessert, breakfast, appetizer, snack, drink, sauce or seasoning | The buttons in the course question, written into notes as shown. |
Missing folders are created on first use. The settings page also shows whether Dataview is installed and has a Regenerate recipe table button.
A template is a note with {{variables}}. In the properties, lists become list properties and numbers stay numbers; text is quoted only when YAML needs it. ingredients: {{ingredients}} writes the nested ingredient groups. Unknown variables are left as they are, so other template syntax survives.
| Variable | Contents |
|---|---|
{{name}} |
The recipe name |
{{from}}, {{sourceUrl}}, {{genre}} |
As answered |
{{description}} |
The description, paragraphs kept |
{{course}} |
The course, as a one-item list |
{{servings}} |
A number, or the text typed |
{{prepTime}}, {{cookTime}}, {{totalTime}} |
e.g. 1 hour 15 minutes |
{{ingredients}} |
The nested ingredients property (in the note text: a short summary) |
{{ingredientChecklist}} |
The checklist with its markers |
{{instructions}} |
A numbered list |
{{notes}} |
The notes (a list when there are several lines) |
{{tags}}, {{goesWellWith}} |
Lists |
{{photo}}, {{photoEmbed}} |
[[link]] to the photo, and ![[photo|400]] |
{{details}} |
The "From · Servings · Prep · Cook · Total" line |
{{mocLink}} |
A link back to the recipe MOC |
{{date}}, {{time}}, {{date:FORMAT}} |
When the note is created (moment.js formats, e.g. {{date:YYYY-MM-DD HH:mm:ss}}) |
The plugin goes online in one case only: when you give a photo's web address, it downloads that image once (with Obsidian's requestUrl) and saves it in your vault. Nothing else is sent anywhere; there's no telemetry and no account. Everything else happens in your vault.
npm install
npm run dev # rebuilds on every change and copies the build into test-vault/ (for the Hot Reload plugin)
npm run build # type check and release build
npm run lint # ESLint with eslint-plugin-obsidianmd
npm test # tests with a stand-in for Obsidian and an in-memory vault; no network
npm run check:dataview -- "Recipes/Recipe MOC.md" # runs a note's Dataview tables with test-vault's copy of Dataview and lists rows a table would drop
test-vault/ is a local Obsidian vault for trying the plugin; it isn't part of the repository.