Notion Formulas 2.0 represents a major milestone for code inside the no-code app. 2.0 includes a substantial rewrite of the 1.0 language. It introduces some important new powerful concepts to the language and makes a lot of difficult solves in the previous version much more pleasant to achieve.
Below you’ll find the key new concepts introduced in 2.0. You can also see a lot of these new concepts in action in my latest YouTube video (like and subscribe, you heathen!), but this article gets into some meatier details and code samples.
If this all seems patently terrifying to you, you should definitely sign up to get notified when my Formulas Fundamentals 2.0 course ships.
Major language changes
No more prop (kinda)!
One of the most major changes is that we partially will no longer use the
prop function in Notion Formulas 2.0. Previously when we wanted to reference other properties in our database, we would write something like this…
Formulas 2.0 introduces property tokens, and you more simply refer to them directly by name (and not as a string):
In the editor, property tokens looks more like the image below, rendered with a grey background.
You’ll also note the addition of a live-preview area in the formula editor. This shows you a real-time preview of the formula result using the current “selected” page you open the formula editor from. More on this later on this page.
- A property token
- The live-preview area
- The documentation examples are now interactive and integrated (note that the examples use my property). You can click the page icon to copy the example to your clipboard.
One caveat here (and the reason I say “kinda”) is that in order to copy and paste into Notion formulas, you will have to copy a
prop function call. So to share formulas in the wild, you’ll use
prop still (and why I’ll be leaving
prop in almost all my examples).
You can also type out a
prop reference in the editor and it will be immediately translated into a property token.
You also get
prop references in your clipboard when you copy property tokens from the Notion editor.
It’s pretty confusing, and a bit clunky. For now, know that I tend to leave in
prop in examples because I want them to be copy and pasteable, but if you are typing your own code, you will largely no longer use
prop (at least not directly).
Formulas 1.0 were mostly a functional programming language.
In the above example we are calling the
prop function with the parameter
"Date", a string. We are passing the result of that function call to the
date function, which returns the day of the month from the provided date.
Formulas 2.0 introduces some concepts from object-oriented programming. While we can still do the following in Formulas 2.0, using a property token instead of a call to
…we can now access the date function on the Date property object directly, using “dot-notation”:
In the former, the
Date property is passed as an argument to the
In the latter, we are calling the same
date function, however, we’re calling it as a function on the
Date object, so we don’t need to pass it in as the argument.
In short, you can generally call functions in two different ways in Formulas 2.0:
/** * In the functional call, we pass the object we want to operate * on as the first argument */ dateAdd(Date, 1, "years")
/** * In dot-notification style, we drop the first argument and call the * function on the object we would typically pass as the first argument. */ Date.dateAdd(1, "years")
While these look different, they will both end up calling the exact same
dateAdd function with the same arguments, so the return result will be the same in both cases. Sometimes it boils down to your preference.
However, for more complex nested function calls, dot-notation makes for a more elegant writing experience, as well as reducing the possibility that we forget to close an errant parenthesis.
/* Formula 1.0 */ dateAdd(dateAdd(prop("Date"), 1, "years"), 2, "days") /* Formula 2.0 functional syntax (no more prop!) */ dateAdd(dateAdd(Date, 1, "years"), 2, "days") /* Formula 2.0 object syntax */ Date .dateAdd(1, "years") .dateAdd(2, "days")
With dot-notation style, each call to
dateAdd returns a
Date object, so we can then call any methods defined on the date object again, allowing us to chain our function calls together with the
Looking at the example above, you can see how the dot-notation style is a bit more readable and feels like more of a linear left-to-right-read statement than the functional counterparts above it. It reads more like instructions:
- Take the
- Add 1 year to it
- Then add 2 days to that
You can define arbitrary lists:
/* From the list, return the element at the 0 index (which is 1) */ [1, 2, 3].at(0)
/* Returns another list multiplying each item in the array by 2 ([2, 4, 6]) */ [1, 2, 3].map(current * 2)
Some list functions (such as
map above) that operate on lists receive an expression argument in which you can use the provided values
index. Essentially this means that each item in the list is passed to the expression as
current and for each “tick” of the loop,
index is incremented by one (starting at a zero index).
Array.map, this is quite similar, but Notion Formulas do not have a callback function wrapper, just an expression.
Of course, dot-notation chaining works for lists as well so we could get the
last item from the
[2, 4, 6] array above like so:
/* Returns 6 */ [1, 2, 3].map(current * 2).last()
One of the most powerful new functions in Notion Formulas 2.0 is
parseDate. In Formulas 1.0, when you needed a specific date in your formula, you had to do something like this.
/* 1691709578 seconds since Jan 01, 1970 */ fromTimestamp(1691709578000)
This allows us to do simpler dynamic date creation as well…
/* We don't need a time and time-zone necessarily */ parseDate("2023-08-10")
Or move around in time…
/* First of the current month */ parseDate(formatDate(now(), "YYYY-MM"))
/* End of the current month */ now() .dateAdd(1, "months") .formatDate("YYYYMM") .parseDate() .dateSubtract(1, "days")
Comments and newlines
You may have noticed the notes in between
*/ above. These are comments and they are now supported in Notion formulas as are newlines! Whitespace is also ignored in a lot of cases, so we can write formulas over multiple lines and align the chained function calls in a visually pleasing way. Comments are ignored when calculating your formulas and you can use them almost anywhere in the formula statement:
/* End of the current month */ now() .dateAdd(1, "months") /* Go to next month */ .formatDate("YYYYMM") /* Format to get just month and year */ .parseDate() /* Parsing will default the day to the 1st */ .dateSubtract(1, "days") /* Go back a day to last day of previous month */
Bonus: because the 2.0 editor supports writing formulas over multiple lines, the above code is 100% legit and could be pasted into the editor without breaking anything.
Instead of having to repeat the same operation over and over, you can now use the
let (and it’s multi-variable partner
lets) function to define reusable variables in your formulas.
let has the signature:
let(variableName, variableValue, expression)
variableNameis what we want to refer to the variable later as
variableValueis the value we want the variable to contain
expressionis the formula code we can leverage the variable inside
let(var, 2 + 2, var * var)
Above we store the result of
2 + 2 ⇒
4 in the variable called
var. We can then use
var as much as we like in the
expression. In this case I did
var * var ⇒
Below we show how
parseDate combined with
let makes date-based calculations obscenely more succinct with Formulas 2.0. These two formulas show a
Next Birthday formula given a date property called
My eyes! But seriously, back with no line breaks, this is really tough to read.
dateAdd(prop("Birthday"), year(now()) - year(prop("Birthday")) + if(toNumber(formatDate(now(), "M")) > toNumber(formatDate(prop("Birthday"), "M")), 1, 0) + if(toNumber(formatDate(now(), "M")) == toNumber(formatDate(prop("Birthday"), "M")), if(toNumber(formatDate(now(), "D")) > toNumber(formatDate(prop("Birthday"), "D")), 1, 0), 0), "years")
Wow, so much nicer! And look at that gorgeous line breaking and indenting!
let(birthday, parseDate(now().year() + "-" + prop("Birthday").formatDate("MM-DD")), let(today, parseDate(now().formatDate("YYYY-MM-DD")), birthday.dateAdd(today > birthday ? 1 : 0, "years") ) )
And here’s how that works…
- We use
prop("Birthday").formatDate("MM-DD")to get the month and the day of the birthday, example, May 31st would be “05-31”.
- We add to the front of that the current year with
now().year(). In this example, this would result in “2023-05-31”.
- We use
parseDateto turn that string version of a date into a date object and store the result in the
- We set another variable called
now()to get the current date formatted with
parseDateagain turns it back into a date object. This effectively converts
now()to the beginning of the day.
- Finally we add 1 year to the
birthdayvalue if it has passed.
We can also define multiple variables with
lets which lets you pass in as many var/value pairs as you like before providing the final expression.
lets(var1Name, var1Value, var2Name, var2Value ..., expression)
So we can rewrite our
let-based example above like so:
lets( /* Set "birthday" (first argument) to the value in the second argument */ birthday, parseDate(now().year() + "-" + prop("Birthday").formatDate("MM-DD")), /* Set "today" (third argument) to the value of the fourth argument */ today, parseDate(now().formatDate("YYYY-MM-DD")), /* In the expression, we can use the "birthday" and "today" variables we've defined */ birthday.dateAdd(today > birthday ? 1 : 0, "years") )
if function remains and a new
ifs function ships with 2.0. You can think of the
s standing for “switch”. This allows you to drop the old nested
if statements and supply pairs of conditions and returns. It works a lot like
lets does for
For example, let’s say you have a property called
Count and wanted to display a
Size (if) formula based on this count with the following ruleset:
- count greater or equal to 10, output “Large”
- count between 5–10, output “Medium”
- count greater than 0 but less than 5, output “Small”
- otherwise, output “None
Here’s how we’d do that with
if(Count >= 10, "Large", if(Count >= 5, "Medium", if(Count > 0, "Small", "None")))
Now that we can add new lines in our formula, we can make this a bit easier to read by adding new lines (by typing
enter) and indenting (by typing
In Formulas 1.0 this would have broken our formula. Now this is totally valid!
if( Count >= 10, "Large", if( Count >= 5, "Medium", if( Count > 0, "Small", "None" ) ) )
ifs allows us to make this even easier!
ifs( /* LARGE */ Count >= 10, "Large", /* MEDIUM */ Count >= 5, "Medium", /* SMALL */ Count > 0, "Small", /* DEFAULT */ "None" )
Well, gosh, that’s substantially easier to read! With
ifs we can specify an argument pairing of condition/result, so we don’t have to nest each pair of conditions, we just add another condition and result value. The final argument can be provided as a “default”.
If return types no longer have to match
In 1.0, the following would raise a
Type mismatch error because
"A" are not the same type.
if(now() > prop("Birthday"), 1, "A")
In Formulas 2.0, you can write this safely. Be aware though that in this case the data will be coerced to a string, so you will not be able to use number formatting on the. There’s a sneaky workaround for this though which involves setting the format prior to turning the property into a formula.
Styling rich text
What if in my last example I wanted my sizes to have a different look? Enter
style (and its counterpart
Wow! How cool is that?!
style takes a list of “style” strings and formats the string as rich text in the same way you might manually style text using Notion’s text editor. You can use any of Notion’s basic text styling and color combos.
ifs( Count >= 10, "Large".style("b"), Count >= 5, "Medium".style("i", "yellow_background"), Count > 0, "Small".style("u"), "None".style("red") )
You can also remove any styles applied to text with
When using a
Person property in Formula 1.0, the output would be turned into an email address. This meant that it wasn’t possible to filter on the currently viewing user when using formulas. Now we can manipulate these properties as lists and access the account’s email and name using the functions of the same name.
- Below we can see a
firston that property, returning the first person in the list.
- We can use
- We can use
nameto get the name of a Notion user in our formulas in
We also no longer need to create advanced properties such as
Last edited by to access these properties in our formulas! In databases, Formulas 2.0 now have the following properties available to us on page references:
Created By– Returns a single Person object
Created Time– Returns a Date object
Last Edited By– Returns a single Person object
Last Edited Time– Returns a Date object
It should be noted that the
Created By and
Last Edited By properties return single Person objects, whereas
Person properties return a List/Array of
Person objects (which is why I used
first() on the
Person property in the table view above). Also, note the case difference here.
Last edited by is used for the Property type, whereas
Last Edited By is used in formulas.
You can now refer to properties of related pages directly in formulas. Which means you almost don’t need rollups any more unless you plan to filter on them.
For example, if I had a
Weekly Agenda database that was related to a
Journals database with a
Miles Ran number property, I could get the
sum of the miles ran on the weekly level with:
You can do a ton with relations as lists now, including filtering. Definitely recommend checking out the video at the top of this blog for more ideas.
2.0 has added a number of programming-style operators. For example, if you wanted to created a conditional that tested two conditions, in 1.0, you might write one of:
or(true, false) /* => false */
and(true, false) /* => false */
true || false
true && false
The formula editor, while still requiring a single encapsulated formula now supports writing formulas over multiple lines and a live-preview. You can discover more about the editor by watching my video on the overview of Formulas 2.0.
Live database updates
The live-preview feature is new in the Formulas 2.0 editor. In 1.0, you would see the output in real-time in the database column itself. In Formulas 2.0, you can see a preview of the changes before saving, but we can no longer see the updates to multiple records in real-time.
While this does represent a loss in functionality, this makes Formulas 2.0 much faster to write than Formulas 1.0! You should notice considerably less “lag” as you type.
Other Language Updates
Formulas 2.0 is basically a superset of the vast majority of 1.0, but it is not backwards compatible.
Notion has done a fantastic job of automatically converting 1.0 syntax into 2.0 syntax, but your existing knowledge may not map to the new language. 2.0 removed many functions, changed others, and added a load of new concepts.
Formula Fundamentals 2.0
If any or all of this sounded like a foreign language to you, you can check out the new version of my Formula Fundamentals course. I’m re-writing it from the ground up for Formulas 2.0.
You can get on the wait-list and get a 20% off coupon by signing up at notionmastery.com/formulas.
Note: Notion Mastery students get free access to this course, support with custom formulas through our forums and office hours. You might want to join!