> ## Content Index
> Fetch the complete content index at: https://notionsolutions.net/llms.txt
> Use this file to discover other available public pages before exploring further.

# Notion Formula let() and lets(): Syntax, Scope and Client Recipes
- URL: https://notionsolutions.net/notion-formula-let/
- Published: 2026-10-29T15:00:00.000Z
- Updated: 2026-10-29T14:59:59.000Z
- Description: let() stores a value under a name for one Notion formula, and lets() does it for several. The syntax in both forms, when to pick lets(), the parentheses scope rule, budget, days-left and contact-name recipes, the mistakes that stop a formula saving, and how it differs from Define variables.
- Author: Kevin Turner
- Tags: guide, databases

**In a Notion formula, `let(name, value, expression)` stores a value under a name you choose, and the expression after it can use that name as often as it needs.** `lets()` does the same for several names: pairs of name and value, then one final expression. A name exists only inside the parentheses of the call that created it. Notion's [formula syntax reference](https://www.notion.com/help/formula-syntax?ref=notionsolutions.net) shows `let(person, "Alan", "Hello, " + person + "!")` returning "Hello, Alan!".

For a formula that repeats one value, such as a subtotal or a days-left number, write `let()`. For two or more names, I'd write `lets()`, because it's the form Notion's reference documents for several variables. The patterns below are built for a client or task database, with property names you swap for your own. For the rest of the 2.0 language, from tokens to lists, [the Formulas 2.0 overview](https://notionsolutions.net/notion-formulas-2-0/) covers what changed.

## The let() and lets() syntax, argument by argument

Notion's [guide to the Formulas 2.0 changes](https://www.notion.com/help/guides/new-formulas-whats-changed?ref=notionsolutions.net) describes the arguments of `let()` in order: the first is the variable's name, the second is its value, and the third is where you write the rest of the formula using that name. Whatever that third argument works out to is the formula's result.

```
let(name, value, expression)
lets(name1, value1, name2, value2, ..., expression)
```

`lets()` repeats the name and value pair as many times as you need, and the last argument is always the expression. That gives you a quick check when a formula won't save: a one-name `let()` has three arguments, and a `lets()` always has an odd number, because every name needs a value and one expression closes the call.

A later pair can use a name from an earlier one. In the duration example on Thomas Frank's [lets() reference page](https://thomasjfrank.com/formulas/functions/lets/?ref=notionsolutions.net), `hoursCount` is defined first and then used to build the variables after it. I'd order the pairs the way you'd explain the calculation out loud: the raw value, then anything built from it, then the result, since that's also the order someone checking the formula will read it in.

### Function form or method form

Both functions also have a method form, with the name in front of a dot. Ben Smith's [let() reference](https://bensomething.com/notion/formulas/functions/let?ref=notionsolutions.net) lists `variable.let(value, expression)`, and Frank's lets() page lists `variable.lets(value, variable, value, ..., expression)`. That matches the general dot-notation rule in Ben Borowski's [Notion Mastery walkthrough of Formulas 2.0](https://notionmastery.com/everything-new-in-notion-formulas-2-0/?ref=notionsolutions.net): the first argument moves in front of the dot and the function stays the same. Every `let()` and `lets()` example in Notion's syntax reference uses the function form, so that's the form used here. Pick one style per formula; switching between them makes the pairs harder to count.

[![Two Notion formulas with their arguments labelled: let(person, "Alan", "Hello, " + person + "!") split into name, value and expression with the result "Hello, Alan!", and lets(base, 3, height, 8, base * height / 2) split into two name and value pairs and a final expression with the result 12, five arguments in all](https://storage.ghost.io/c/5c/30/5c30c46b-86a3-4d20-984f-4f6ae8ad1ed2/content/images/2026/10/notion-formula-let-syntax-anatomy.webp)](https://storage.ghost.io/c/5c/30/5c30c46b-86a3-4d20-984f-4f6ae8ad1ed2/content/images/2026/10/notion-formula-let-syntax-anatomy.webp?ref=notionsolutions.net)

Each name comes before its value, the expression always comes last, and the names work only inside the call's parentheses.

## Should you write let() or lets()?

Write `lets()` whenever a formula has two or more names. The sources disagree only on whether `let()` can also take several, and `lets()` is right under every version of the documentation.

- Notion's syntax reference says `let()` "Assigns a value to a variable" and `lets()` "Assigns values to multiple variables", and each of its `let()` examples uses a single name.
- Thomas Frank's [let() reference page](https://thomasjfrank.com/formulas/functions/let/?ref=notionsolutions.net) says that since April 2025 `let()` accepts several variables, "making it identical in functionality to lets", and credits Ben Borowski with pushing for the change. The same page calls `lets` an alias of `let()`.
- Frank's lets() page and his [page on formula variables](https://thomasjfrank.com/formulas/variables/?ref=notionsolutions.net) still describe the older split: `let` for one variable, `lets` for several.

A multi-name `let()` in a copied template is valid by Frank's account and outside what Notion's reference describes. Renaming it to `lets()` settles that either way. Frank's lets() page points at the same fix: if you're defining several variables and hit an error, check that you're using `lets()` rather than `let()`, a mistake he calls common when a formula starts with one variable and grows. It's a one-word change, so I'd make it before looking anywhere else.

## Where a variable exists: scope and nesting

A name can be used only between the parentheses of the `let()` or `lets()` that created it. Frank states this on both of his reference pages, and it decides where your closing parenthesis goes: everything that uses the name has to sit before it.

You can also put one call inside another. An outer name is visible inside the inner call; an inner name isn't visible outside it. Frank's let() page shows the first half:

```
let(firstName, "Monkey", let(lastName, "Luffy", firstName + " D. " + lastName))
```

This returns "Monkey D. Luffy", because the inner call reads `firstName` from the outer one. When the inner call reuses an outer name, the inner value wins inside that call:

```
let(lastName, "Luffy", "Monkey D. " + let(lastName, "Garp", lastName))
```

This returns "Monkey D. Garp". Outside the inner parentheses, `lastName` is still "Luffy". Nothing fails here, so in a long client formula the reuse can go unnoticed while the result quietly shows the inner value. Give every name in a nested formula its own spelling. An inner `lets()` can also supply the value of an outer name, which the last row of the results table below works through step by step.

[![Two nested-box diagrams of Notion let() formulas: in the first, firstName from the outer let() is used inside the inner let(), which returns Monkey D. Luffy; in the second, an inner let() redefines lastName as Garp, so the formula returns Monkey D. Garp and the outer value Luffy goes unused](https://storage.ghost.io/c/5c/30/5c30c46b-86a3-4d20-984f-4f6ae8ad1ed2/content/images/2026/10/notion-formula-let-scope-nesting.webp)](https://storage.ghost.io/c/5c/30/5c30c46b-86a3-4d20-984f-4f6ae8ad1ed2/content/images/2026/10/notion-formula-let-scope-nesting.webp?ref=notionsolutions.net)

An outer name is visible inside a nested call; a nested call that reuses the name gets its own value until its parenthesis closes.

### Keep the outer current inside a nested list function

List functions such as `map()` and `filter()` give you `current` for the item being processed, and a list function nested inside another brings its own `current`. Frank's lets() page uses a variable to keep the outer one: inside `filter()`, it stores `current` as `currentList`, so the inner `map()` can still reach the list it came from.

```
[[1, 3, 5, 7], [2, 4, 6, 8], [1, 2, 3, 6]].filter(lets(
  currentList, current,
  remainders, current.map(currentList.sum() % current),
  remainders.every(current == 0)
))
```

A list stays only if every number in it divides the list's sum evenly. Frank doesn't print the result, but the arithmetic settles it: 16 leaves a remainder when divided by 3, 20 leaves one when divided by 6, and 12 divides evenly by 1, 2, 3 and 6, so only `[1, 2, 3, 6]` survives the filter. The same move shows up in client work once a formula walks related pages. Notion's syntax reference lists a relation as a list of pages, so when one list function runs inside another over a [client and project relation](https://notionsolutions.net/notion-relations-and-rollups/), a variable is how the inner call refers to the outer page.

## let() patterns for a client or task database

Each pattern names the value a formula would otherwise repeat. Swap the property names for yours, and open the editor from a row you can check by hand.

### From sales tax to budget left

Notion's own example is a sales tax line. The Formulas 2.0 guide suggests a variable when a total is built from properties you'd rather not add up again each time you type it:

```
let(SalesTax, (prop("Subtotal")*0.099), prop("Subtotal") + SalesTax)
```

The result is the Subtotal plus 9.9% tax, and the tax calculation is written in one place. 0.099 is the rate in Notion's example; use the rate that applies to your invoices.

The same idea gives a project record a budget line. The gap between Budget and Spent appears in the comparison and in the text, so it gets a name:

```
let(
  remaining, prop("Budget") - prop("Spent"),
  if(remaining < 0, "Over by " + format(abs(remaining)), format(remaining) + " left")
)
```

With a Budget of 5000 and Spent of 3200, the result is "1800 left". With Spent at 5600, `remaining` is -600 and the result is "Over by 600", because `abs()` drops the minus sign before `format()` turns the number into text. The formula reports the gap. Whether an overrun calls for a talk with the client is your decision.

### Days until due, with one status label

Without a variable, a days-left label repeats the same `dateBetween()` in every check and again in the text. With `lets()` it appears once:

```
lets(
  /* Days from today to Due: positive ahead, 0 on the day, negative after */
  daysLeft, dateBetween(prop("Due"), today(), "days"),
  /* "day" or "days" for the label */
  unit, if(daysLeft == 1, " day", " days"),
  ifs(
    prop("Status") == "Done", "Done",
    daysLeft < 0, "Overdue",
    daysLeft == 0, "Due today",
    format(daysLeft) + unit + " left"
  )
)
```

The sign comes from the argument order. Thomas Frank's [dateBetween() reference](https://thomasjfrank.com/formulas/functions/datebetween/?ref=notionsolutions.net) says the result is positive when the first date is later than the second, so with Due first, a task due next week gets a positive number and an overdue task, whose Due is earlier than today, a number below zero. `today()` returns the date without the time, so for a Due date without a time, a task due tomorrow shows "1 day left" and one due today shows "Due today". The `unit` pair borrows the plurality trick from Frank's let() page, which adds "s" only when the count isn't 1\. `ifs()` returns the value for the first true condition, which is why the Status check comes first: a finished task reads "Done" even after its date has passed.

Two cases sit outside this formula. If Due includes a time, or you'd count both the first and the last day, the [dateBetween off-by-one guide](https://notionsolutions.net/notion-datebetween-off-by-one/) sets the counting rule. A blank Due needs its own first condition, and the right label depends on where the result goes; the [guide to Notion formula date errors](https://notionsolutions.net/notion-formula-date-errors/) covers empty and text inputs. Which status counts as finished is your call, and the formula checks only the one you type in.

### A cleaned contact name used twice

A greeting line for a client email needs the contact's name twice: once to check it's there and once in the text. For a Text property called Contact:

```
let(
  contact, prop("Contact").trim(),
  if(empty(contact), "Add a contact name", "Hi " + contact + ",")
)
```

`trim()` removes white space from the beginning and end of text, so " Dana Reyes " gives "Hi Dana Reyes,". A Contact holding only spaces trims to "", which `empty()` counts as empty, so the row shows the prompt instead of a greeting with a gap. If the names also arrive in the wrong case, the [first-letter capitalization formulas](https://notionsolutions.net/notion-formula-capitalize-first-letter/) deal with stray spaces and capitals together, and their result belongs in the `contact` value so the check and the greeting read the same cleaned name.

## Lay out a long lets() so it still reads next month

The formula editor takes several lines, tabs and comments, which Notion's Formulas 2.0 guide lists among the changes. Notion Mastery adds that comments are ignored when the formula calculates and can go almost anywhere in it. That makes one comment per pair practical. I'd write any `lets()` longer than a line with one pair per line, a comment above each saying what the value means, and the expression last, so whoever opens it next meets each name before the formula uses it:

```
lets(
  /* Amount before tax, from the invoice row */
  amount, prop("Subtotal"),
  /* Rate from Notion's example; use your own */
  tax, amount * 0.099,
  /* Result: total with tax */
  amount + tax
)
```

This returns the same total as Notion's SalesTax formula above, with the subtotal and the tax each named.

## When a let() formula won't save or returns the wrong value

Notion's [intro to formulas](https://www.notion.com/help/formulas?ref=notionsolutions.net) says the field at the top of the editor tells you what's missing or what the formula expects. The table covers the causes that come from `let()` and `lets()` themselves.

__Common let() and lets() mistakes and how to fix them__
| Pattern in the formula                                                   | What's wrong                                                                                                                                                                                                                                                                          | Fix                                                                                     |
| ------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------- |
| A name is used after the closing parenthesis of the call that created it | The name exists only inside those parentheses.                                                                                                                                                                                                                                        | Move that parenthesis to the end, so every use of the name sits inside it.              |
| lets(amount, prop("Subtotal"), tax, amount \* 0.099)                     | The call ends on a value; there's no expression after the last pair, and the argument count is even.                                                                                                                                                                                  | Add the expression: lets(amount, prop("Subtotal"), tax, amount \* 0.099, amount + tax). |
| Several name and value pairs inside let()                                | Notion's reference documents let() for one variable; only Frank's let() page describes the multi-variable form.                                                                                                                                                                       | Rename let to lets.                                                                     |
| A nested call reuses an outer name                                       | The inner value wins inside the inner call, so the result shows it instead of the outer one.                                                                                                                                                                                          | Give the inner variable its own name.                                                   |
| A number variable joined to text with +                                  | Notion's [guide to writing formulas](https://www.notion.com/help/guides/write-formulas-that-extend-capabilities-of-databases?ref=notionsolutions.net) says numbers are combined with text by first converting them, and lists mixed data types as a cause of a "Type mismatch" error. | Wrap the number in format() before joining, as the recipes above do.                    |

A date variable needs one more check: its value has to be a Date. A date-looking value can arrive as Text, or as a List from a rollup, which Notion's syntax reference says can hold a number, a date or a list. The date errors guide linked above traces the input type before anything else.

## let() variables vs. the Define variables action

Buttons, database buttons and database automations have an action called Define variables, and it does a different job. Notion's [database automations help page](https://www.notion.com/help/database-automations?ref=notionsolutions.net) says it creates a custom variable from mentions and formulas that you can use in other automation actions. You add it with New action or Add action → Define variables, click Variable 1 to name it, and fill the value with @ for a mention or ∑ for a formula. The help pages for [buttons](https://www.notion.com/help/buttons?ref=notionsolutions.net) and [database buttons](https://www.notion.com/help/database-buttons?ref=notionsolutions.net) describe the same action as a button step. Notion's automation example defines Variable 1 as `Trigger page.Sub-item`, then points an edit action at Variable 1, so marking a parent task complete also marks its sub-items complete.

The difference is where the name lives. A `let()` name exists inside one formula's parentheses, and nothing outside that formula can read it. A Define variables value is a step in a button or automation, and the actions you add after it can use it. For a label shown in a property, I'd keep the name in the formula with `let()`. When a later action has to find or edit pages, as in Notion's sub-item example, Define variables is the tool. The automation runs the edit, but which pages it may touch is a rule you set and check; [planning the trigger, action and exception](https://notionsolutions.net/notion-automations-trigger-action-exception/) covers that before you build. If you're still choosing what should start the run, the [buttons vs. automations comparison](https://notionsolutions.net/notion-buttons-vs-automations/) starts there.

Plans differ too. Notion's help says database automations are available on paid plans, with Free plan users limited to creating Slack notification automations, while buttons work on all plans and reserve certain actions for paid ones.

## Results for each let() and lets() example

These are the published let() and lets() results from Notion's syntax reference, Ben Borowski's Notion Mastery walkthrough and Thomas Frank's reference pages: eight formulas from three sources. Seven results are printed next to their formulas; the last one isn't, and its working is under the table. Paste any of them into a formula property opened from a row and compare the live preview with the result column.

__let() and lets() examples with their documented results__
| Formula                                                                                              | Result          | What it shows                                                        | Source                                        |
| ---------------------------------------------------------------------------------------------------- | --------------- | -------------------------------------------------------------------- | --------------------------------------------- |
| let(person, "Alan", "Hello, " + person + "!")                                                        | "Hello, Alan!"  | One name, used inside the expression.                                | Notion syntax reference                       |
| let(radius, 4, round(pi() \* radius ^ 2))                                                            | 50              | The name works inside nested functions.                              | Notion syntax reference                       |
| let(var, 2 + 2, var \* var)                                                                          | 16              | The variable holds the result of 2 + 2, so the expression squares 4. | Notion Mastery                                |
| lets(a, "Hello", b, "world", a + " " + b)                                                            | "Hello world"   | Two pairs, then one expression.                                      | Notion syntax reference                       |
| lets(base, 3, height, 8, base \* height / 2)                                                         | 12              | The same shape with numbers.                                         | Notion syntax reference                       |
| let(firstName, "Monkey", let(lastName, "Luffy", firstName + " D. " + lastName))                      | Monkey D. Luffy | An outer name is visible inside an inner call.                       | Thomas Frank, let()                           |
| let(lastName, "Luffy", "Monkey D. " + let(lastName, "Garp", lastName))                               | Monkey D. Garp  | An inner name overrides the outer one inside its call.               | Thomas Frank, let()                           |
| lets(var1, 2 + 2, var2, 3 + 3, var3, lets(var4, var1 \* var2, var5, var4 / var1, var5), var3 - var1) | 2               | An inner lets() supplies the value of an outer name.                 | Thomas Frank, lets(); result worked out below |

The last row step by step: `var1` is 4 and `var2` is 6\. Inside the inner call, `var4` is 4 × 6 = 24 and `var5` is 24 / 4 = 6, and the inner `lets()` returns `var5`, so `var3` is 6\. The final expression is 6 - 4, which is 2\. Notion's SalesTax pattern isn't in the table because its result depends on each row's Subtotal.

## Start with the value you repeat most

1. Open a formula you rely on from a row whose result you already know, so the editor shows a live preview for that row.
2. Find the piece that appears more than once: a subtraction, a `dateBetween()`, a trimmed property.
3. Wrap the formula in `let()` with that piece as the value, and replace each copy with the name.
4. Compare the preview with the result you knew. If they match, the rewrite changed how the formula reads, not what it returns.
5. When a second value needs a name, change `let` to `lets` and add the pair before the expression.

`let()` earns its place the second time a formula repeats itself. For a freelancer with one budget or deadline formula, I'd wrap the repeated piece in `let()` and stop there, because one well-chosen name already makes the formula readable. For a small team keeping several formulas across client databases, I'd move every formula with two or more names to `lets()`, one pair per line with a comment above each, because that's the layout a teammate can check without asking you what it does.

## More questions about let and lets

### Can a variable hold a list, a date or a page?

Yes. Ben Smith's let() reference lists all data types for both what `let()` accepts and what it returns. The examples above store a number, text and a list (`currentList`), and Frank's let() page stores a date in a birthday calculation. A relation's pages arrive as a list, so they can go into a variable the same way.

### Can another formula property use my variable?

No. A name exists only inside the parentheses of the `let()` or `lets()` that created it, so a second formula property can't see it. The second formula can still read the first one's final result with `prop()`, like any other property, and Notion's [page on common formula errors](https://www.notion.com/help/common-formula-errors?ref=notionsolutions.net) counts each reference to another formula or rollup as a layer, up to a limit of 15\. For a value only one formula needs, give that formula its own `let()`.

### A copied formula uses a name that isn't a property. What is it?

Look for it as the first argument of a pair in a `let()` or `lets()` earlier in the formula. If it's `current` or `index`, it's one of the built-in variables that list functions provide; Frank's variables page notes that `index` counts from 0, so the first item in a list is 0.