My Obsidian Recipe Workflow
How I organize recipes and ingredients in Obsidian, with templates, an ingredient picker, and cooking history.
My recipe collection started in Obsidian. I wanted a place to keep ingredient amounts, directions, and the adjustments I make as I cook. Over time, I began keeping ingredients as their own notes and linking recipes to them. That lets me approach the collection from either direction: find a recipe and see what it needs, or start with an ingredient and see what I can make.
Actual Recipe grew out of that collection. I still keep the source notes in my vault, and selected notes become the website’s recipes, ingredients, and articles. Here is how that system is organized, including the parts you can reproduce in your own vault.
Where the Notes Live
My vault has a broader PARA structure. The cooking collection mostly lives in Resources:
3-Resources/
Recipes/
Ingredients/
Cooking/
Recipes.md
Ingredients.md
Templates/
Recipe Template.md
Ingredient Template.md
Daily notes live under 2-Areas/Daily Notes/ and can link to recipes I made that day.
You can use simpler folder names in your own vault. The queries below select notes by tags and links, so those relationships matter more than my folder names.
A Recipe is a Note with a Few Properties
My recipe template supplies an alias, creation date, and tags. It then creates Ingredients, Intro, Directions, and Times made sections.
Here is a shortened example of that structure, with illustrative amounts and directions:
---
alias: Simple rice
created: 2026-10-04
tags: recipe, grain
---
# Simple Rice
## Ingredients
Ingredient:: [[rice, white]] 1 cup
Ingredient:: [[water]] 2 cups
Ingredient:: [[sea salt]] to taste
## Intro
An example of the note structure.
## Directions
1. Combine the ingredients in a saucepan.
2. Bring to a simmer, cover, and cook until the rice is tender.
recipe identifies the note as a recipe. Other tags help group it: my collection includes ingredient or dish categories, vegan, and more. alias gives the note an alternate name. created records when I added it.
Ingredients Get Their Own Notes
An ingredient note can be very small. Mine usually has a creation date, the ingredient tag, one or more category tags, and a query listing recipes that link to it. There is no need to write an encyclopedia entry before an ingredient is useful.
Names distinguish things I want to select separately: rice, arborio, rice, white, and rice, sushi, for example, or different cuts of meat. A recipe links to the appropriate note and keeps the amount alongside the link.
Obsidian’s [[ingredient]] notation creates a wikilink. Reusing a note keeps its connections together. The Ingredient:: prefix is a Dataview inline field.
Adding Ingredients while Writing a Recipe
My recipe template includes an add ingredient button. The Buttons plugin invokes a QuickAdd capture called capture - add ingredient. That capture uses Dataview to collect notes tagged ingredient, opens a Templater selection menu, asks for an amount, and inserts the ingredient field into the active recipe’s Ingredients section.
The process is simple from the editor: choose an ingredient, enter its amount, and repeat. If an ingredient does not exist yet, I can create its note using the ingredient template and then select it.
The automation uses four community plugins: Dataview, Templater, QuickAdd, and Buttons. The underlying recipe remains ordinary Markdown, so you can also type its properties, links, and directions by hand.
Finding Recipes from an Ingredient
Each ingredient note includes a Dataview query under Used in These Recipes. To reproduce it, put the following query in a fenced code block whose language is dataview:
list from #recipe
where contains(file.outlinks, this.file.link)
The query finds recipe notes whose outgoing links include the current ingredient. When I add that ingredient to a recipe, the recipe appears in this list without my maintaining a second list manually.
My ingredient index lists all notes tagged ingredient. My recipe index has tabs for all recipes, vegan recipes, favorites, and the recipes mentioned most frequently in daily notes.
Remembering what I Cooked
The recipe template’s Times made section looks for daily notes that link to the recipe. Its Dataview query is:
LIST
FROM #daily_note
WHERE contains(file.outlinks, this.file.link)
SORT file.cday DESC
Linking a recipe from a daily note gives it a place in my cooking history. The recipe index also uses Dataview JavaScript to count incoming links from daily notes and sort recipes by that count.
These are counts of linked daily notes, so their usefulness depends on what I record. The Times made query sorts by file creation time, which may differ from the date written in a daily note.
Starting with Your Own Vault
On Actual Recipe, Export → As Obsidian Recipe downloads a Markdown note with YAML properties, linked ingredients, an Intro section, and Directions. Its share property starts as False.
Place it in your vault, open it, and create ingredient notes from the links you want to reuse. Add a recipe index and the ingredient reverse-link query when the collection grows. Templates and the ingredient picker can come later, once you know which repeated steps you want to automate.
Install the Plugins
Open Settings → Community plugins, enable community plugins if needed, and use Browse to install and enable Dataview, Templater, QuickAdd, and Buttons.
Dataview supplies the ingredient list and recipe queries. Templater fills in dates and prompts when a note is created. QuickAdd creates the notes and captures ingredient entries. Buttons puts the capture command directly in the recipe.
Set up Templater and the Templates
Create 3-Resources/Templates, 3-Resources/Recipes, and 3-Resources/Ingredients if they do not exist. In Settings → Templater, set Template folder location to 3-Resources/Templates and enable Trigger Templater on new file creation. Templater’s settings documentation explains these options.
Create the two template files below. Copy the contents of each example into its file using Source mode. Keep the triple-backtick lines in the templates.
Ingredient Template.md
Save this as 3-Resources/Templates/Ingredient Template.md:
<%*
const date = tp.date.now("YYYY-MM-DD");
const category = (await tp.system.prompt("Ingredient category, such as vegetable or spice")) || "";
_%>
<% "---" %>
created: <% date %>
tags: ingredient<% category ? ", " + category : "" %>
<% "---" %>
## Used in These Recipes
The ingredient name comes from the new note’s filename. The template prompts for category tags, adds today’s date, and supplies the reverse-link query. For example, create a note named onion and enter vegetable, allium when prompted.
Recipe Template.md
Save this as 3-Resources/Templates/Recipe Template.md:
<%*
const date = tp.date.now("YYYY-MM-DD");
const alias = (await tp.system.prompt("Recipe alias")) || tp.file.title;
const tags = (await tp.system.prompt("Recipe tags, separated by commas")) || "";
_%>
<% "---" %>
alias: <% JSON.stringify(alias) %>
created: <% date %>
tags: recipe<% tags ? ", " + tags : "" %>
<% "---" %>
# <% tp.file.title %>
## Ingredients
## Intro
## Directions
<% "## Times made" %>
The expression near the end produces the Times made heading when Templater runs. Give the recipe a filename that you want to use as its title. The alias and tags prompts then fill in its properties, and the button will work once the capture command below is configured.
These examples reproduce the note-taking structure of my templates. The recipe example keeps the properties needed for organizing recipes in a personal vault.
Create the QuickAdd Template Choices
In Settings → QuickAdd, create two choices of type Template. In older versions, enter a name, select the type, click Add, and open the choice’s gear icon. Newer versions use New choice → Template. The Template choice documentation describes both interfaces.
Use these settings:
| Setting | Ingredient choice | Recipe choice |
|---|---|---|
| Choice name | template - add ingredient | template - add recipe |
| Template path | 3-Resources/Templates/Ingredient Template.md | 3-Resources/Templates/Recipe Template.md |
| File name / File name format | {{VALUE}} | {{VALUE}} |
| Destination folder | 3-Resources/Ingredients | 3-Resources/Recipes |
| Open created file | On | On |
| Link to created file / Append link | Off | Off |
If File name format has a toggle, turn it on. Choose the specific-folder location and add the folder path; disable creating in the same folder as the active file. Register each choice in the command palette using its lightning-bolt icon or Add to command palette setting. You can then assign hotkeys in Obsidian’s Settings → Hotkeys.
Run QuickAdd: template - add ingredient from the command palette. Enter a name such as onion, then answer the category prompt. Run the recipe command in the same way to create a recipe and answer its alias and tags prompts.
QuickAdd supplies the filename and destination; Templater processes the <% … %> expressions. If those expressions remain visible in the created note, check that Templater is enabled and its new-file trigger is on. To process an already-created note containing expressions, run Templater: Replace templates in the active file.
Folder templates are optional: they can apply the appropriate template when you create a blank note directly in a folder. If you enable them, map 3-Resources/Ingredients to the ingredient template and 3-Resources/Recipes to the recipe template. Use the actual folder spelling, 3-Resources. Start by testing the QuickAdd choices before adding another creation method.
Configure the Ingredient Capture
Add a QuickAdd choice of type Capture named exactly capture - add ingredient. Enable its command-palette registration, so the recipe button can call QuickAdd: capture - add ingredient.
Configure the destination and insertion settings:
- Enable Capture to active file. Open the recipe before running the command.
- Enable Insert after and enter
## Ingredientsas the target text. - Enable insertion at the end of that section, so repeated captures build the ingredient list. Leave Consider subsections off.
- Leave creating a missing heading off; the recipe template already provides it.
- Leave task formatting, prepending, and opening another file off.
- Enable Capture format and paste the entire block below into that field, including its opening and closing backtick lines.
The Capture choice documentation describes the active-file destination and insertion options.
```js quickadd
const dv = app.plugins.plugins.dataview.api;
const tp = app.plugins.plugins["templater-obsidian"].templater.current_functions_object;
const ingredients = dv.pages("#ingredient");
if (ingredients.length === 0) {
throw new Error("Create an ingredient note tagged ingredient first.");
}
const selected = await tp.system.suggester(
(item) => item.file.name,
ingredients,
false,
"Choose an ingredient"
);
if (!selected) return "";
const amount = await tp.system.prompt("Amount", "");
if (amount === null || amount === undefined) return "";
return `Ingredient:: ${selected.file.link} ${amount}`;
```
This is the picker from my configured capture, with checks for an empty ingredient collection and cancelled prompts. It queries Dataview and uses Templater’s selection menu and amount prompt. The js quickadd fence tells QuickAdd to execute the code and insert its return value, as described in the inline scripts documentation.
There is no separate user-script file to install for this version of the picker.
Try the Complete Workflow
- Create an ingredient with the QuickAdd ingredient command and confirm it has the
ingredienttag. - Create a recipe with the recipe command and confirm that the date, alias, and tags are filled in.
- Open the recipe in Live Preview or Reading view and click add ingredient. Choose the ingredient and enter an amount. You can also run the capture directly from the command palette.
- Check that an
Ingredient::line appears under Ingredients, before Intro. Repeat to add another ingredient. - Open the ingredient note. Its Dataview query should now list the recipe.
- Link the recipe from a daily note tagged
daily_note. The recipe’s Times made query should list that note.
If the button is visible as plain code, check that Buttons is enabled and that you are viewing the note in Live Preview or Reading view. If it cannot find its command, check the capture’s exact name and command-palette registration. The Buttons documentation explains command buttons.
If the picker omits an ingredient, check its ingredient tag and give Dataview time to index the note. If a query is visible as plain code, check that Dataview is enabled and that the fence’s language is dataview. The ingredient picker does not require Dataview’s JavaScript-query setting; that setting is needed if you also add my recipe-frequency index using dataviewjs.