Make a Nutrition Report for a Meal PlanCook Basic & Pro
Turn a .menu meal plan (or any .cook recipe) into a nutrition report in Cook Editor:
calories, protein, carbs, sugars and fat, for the whole plan, per person per day, and per ingredient.
The report is a plain-text template in your recipe folder, so you can reuse it on every plan and change it however you like.

Where you'll end up: a report for a three-day plan, rendered in Cook Editor.
What you need
- Cook Editor on macOS, Windows or Linux.
- To be signed in with Cook Basic or Cook Pro. The nutrition lookups in the template use Cook's nutrition database, which comes with both plans.
- Rather not write a template? CookBot, the AI assistant in Cook Pro, can write it and fix it for you. See step 5.
1. Open the plan
Open the meal plan (or recipe) you want a report for. Any .menu file works. When you render a report against it,
the template receives the whole plan: the days, the meals, and every ingredient already scaled to the amounts in the plan.
Against a single .cook file, the template gets that recipe's ingredients and metadata instead.

A three-day plan for a family of three, open in the preview.
2. Save a template
Create a file called nutrition-report.md.jinja anywhere in your recipe folder. A reports/ folder keeps things tidy.
The editor finds templates by extension (.jinja, .j2, .jinja2), and the part before it
(.md here) is the format of the report. Paste this in:
{%- set is_plan = plan is defined -%}
{%- set items = plan.all_ingredients if is_plan else ingredients -%}
{%- set agg = aggregate_nutrition(items) -%}
{%- set t = agg.totals.macros -%}
{%- set days = (plan.days | selectattr("date") | list | length) if is_plan else 1 -%}
{%- set days = days if days > 0 else 1 -%}
{%- set people = plan.servings if (is_plan and plan.servings) else (metadata.servings | default(1) | int) -%}
{%- set people = people if people > 0 else 1 -%}
{%- set per = days * people -%}
{%- set per_label = "person per day" if is_plan else "serving" -%}
# Nutrition — {{ metadata.title | default("Meal plan" if is_plan else "Recipe") }}
| | Total | Per {{ per_label }} |
|:---|---:|---:|
| Energy | {{ t.kcal | round }} kcal | {{ (t.kcal / per) | round }} kcal |
| Protein | {{ t.protein_g | round(1) }} g | {{ (t.protein_g / per) | round(1) }} g |
| Carbohydrate | {{ t.carb_g | round(1) }} g | {{ (t.carb_g / per) | round(1) }} g |
| — of which sugars | {{ t.sugar_g | round(1) }} g | {{ (t.sugar_g / per) | round(1) }} g |
| Fat | {{ t.fat_g | round(1) }} g | {{ (t.fat_g / per) | round(1) }} g |
| Fibre | {{ t.fiber_g | round(1) }} g | {{ (t.fiber_g / per) | round(1) }} g |
## Per ingredient
| Ingredient | Amount | kcal | Protein (g) | Carbs (g) | Fat (g) |
|:---|---:|---:|---:|---:|---:|
{% for it in agg.items -%}
| {{ it.ingredient }} | {{ it.amount.value | round(1) }} {{ it.amount.unit }} | {{ it.macros.kcal | round }} | {{ it.macros.protein_g | round(1) }} | {{ it.macros.carb_g | round(1) }} | {{ it.macros.fat_g | round(1) }} |
{% endfor %}
{% if agg.failures %}
## Not counted
These ingredients couldn't be matched, so they're left out of the totals above:
{% for f in agg.failures -%}
- **{{ f.ingredient }}** — {{ f.error.message }}
{% endfor %}
{% endif %}
What it does:
plan.all_ingredientsis every ingredient in the plan, already scaled. For a single recipe the template usesingredientsinstead, so the same file works for both.aggregate_nutrition(items)looks each ingredient up in the nutrition database and adds them up:agg.totals.macrosfor the totals,agg.itemsfor the per-ingredient rows.- The "per person per day" column divides the totals by the number of dated days in the plan times the plan's
servings. For a recipe, it divides by the recipe's servings. - Anything the database couldn't match ends up in
agg.failures, which gets its own section at the end (see step 4).
3. Render it
With the plan open, open the command palette (⌘ ⇧ P on macOS, Ctrl Shift P on Windows and Linux) and run Cooklang: Render Report…. You can also right-click in the open file.

Cooklang: Render Report… in the command palette.
Pick your template. Every template in your recipe folder is listed under Workspace Templates, and the editor's own Ingredients List and Shopping List are listed under Built-in Templates.

Your templates, then the built-in ones.
The report opens in its own tab, with buttons to print it and to export it as PDF or PNG. Edit the template, render again, and the report updates. Markdown templates can also draw charts with Mermaid: the report at the top of this page adds a pie chart of macros by weight.
4. Ingredients the database can't match
Nutrition lookups work from the ingredient's name, quantity and unit. Some can't be matched: a brand name, a
homemade component, or a unit with no weight behind it, like "1 handful". aggregate_nutrition doesn't stop at those.
It adds up everything it can match and returns the rest in agg.failures. That's why the template above
lists them under Not counted instead of failing, and why the totals are really a floor.
To bring an ingredient in:
- Name it plainly:
@rice{200%g}rather than@Grandma's rice mix{1%bag}. - Give it a unit with a weight or volume behind it.
@salmon{150%g}(cooked)also passes the preparation. - For something truly your own, keep your numbers in a
db/folder and read them withdb(). See Your own data with db().
Per-person numbers look far too high? On a menu line, (×3) scales the whole recipe three times,
not to three servings. If a recipe that serves 4 is scaled ×3 for three people, the plan counts 12 servings.
Check that the scaling in the plan matches how many people are actually eating, and that the plan's servings is set.
5. Let CookBot write itCook Pro
Nutrition reports come with Cook Basic, and you write the template yourself. With Cook Pro, CookBot in Cook Editor can write the template for you, fix it when a recipe trips it up, and make a better-looking version. Open the plan, open the AI chat, and ask in plain words:
- "Write a report template that shows calories, protein, carbs, sugars and fat per ingredient for this menu, with totals per person per day."
- "This report leaves out the Sirniki. Fix the template, or tell me what to change in the recipe."
- "Make an HTML version of this report with a cleaner design I can print."
CookBot proposes the template as a file for you to review. Apply it and it's saved in your recipe folder
(under config/reports/), where you can keep editing it by hand. Compare plans on the pricing page.
Where to go next
- Nutrition functions: vitamins, allergens, confidence, and per-nutrient totals.
- Meal plan reports: everything in the
planvalue, plus per-day totals and variety counts. - Checks: turn the report into pass/fail targets, like "at least 25 g protein per person per day".