logo
FlexLists
Help · Fields

Formulas

A Formula field computes its value from the other fields of the same row — days since a date, a price with VAT, a full name, a list of tags. The function names are Airtable's, so a formula you know from there works here.

1. Writing a formula

Add field → Formula, then type the expression. Refer to a field with its name in braces: {Amount} * 1.21. Type { or the start of a name and the editor suggests fields and functions, with what each gives (number, text, date, …). Inside a function it shows its arguments, the one you are typing highlighted.

Text goes in quotes ("done"), & joins text, + - * / % calculate, = != < <= > >= compare, &&, || and ! mean AND, OR and NOT. A list of values is written [1, 2, {Price}]; multi-select, multiple-user and file fields are lists too.

A mistake is underlined where it is, with what is wrong, e.g. DATEADD expects a date as argument 1; {Intake} is text. While you type, the result shows on three rows of your list (choose which), formatted as the column will show it.

Linked rows. A Link to another list is a list of the rows it links to (each shows as its name). Inside FILTER, MAP, SORT, REDUCE, ANY and ALL over it, item.Field reads a field of the linked row: COUNTA(FILTER({Tasks}, item.Status = "Open")), SUM(MAP({Lines}, item.Qty * item.Price)), SORT({Tasks}, item.Due). The value follows when a linked row or a link changes, without editing the row.

Pasted from Excel? =SUMIF(…), =AVG(…), =IFS(…) and =CONCAT(…) work as they are (the = is dropped). Cell references like A1 or B2:B10 do not: use the field, {Amount}.

TODAY() and NOW() keep the field up to date by themselves: formulas with TODAY update daily, with NOW every 15 minutes.

2. Result types

The editor works out what a formula gives, and the column becomes that type, so it sorts, filters and shows like one:

ResultWhat it can doExample
NumberSorts, filters and totals as a number; decimals, percentage or a currencyROUND({Price} * 1.21, 2)
TextEverything text can doTRIM({First name} & " " & {Last name})
True / falseA checkbox (or Yes / No); filters "is" / "is not"AND({Paid}, {Amount} > 100)
Date, date and timeDate filters; calendar, timeline and Gantt views; your date format and timezoneDATEADD({Start}, 14, "days")
List of valuesChips (or joined); filters "has any / all / none of"FILTER({Tags}, item != "internal")

Under the preview you choose how the result is shown: decimals, percentage or currency; a date format and timezone; a checkbox or Yes / No; chips or text.

3. Let AI write it

Click ✦ Describe it instead and say what you want, e.g. days in shelter: from intake to adoption date, or until today if not adopted yet. Flexlists writes the formula, checks it against your fields and shows it with an explanation and the result on three rows: Use it, Edit or Try again. A formula that does not work is never shown. Your description is saved with the field (✦ written by AI from: …), so you can change it and regenerate later, also after renaming a field.

✦ Explain tells you in plain words what any formula does. When a formula gives an error such as #VALUE!, ✦ Fix it proposes a correction and shows what changes before you apply it.

Privacy: with Send sample values on (the default) up to three rows' values go with the request, so the AI sees what your data looks like; switch it off to send only the names and types of the fields. The formula AI is included in every plan, up to 30 requests per person per hour. Dave (the assistant) and AI agents (MCP) use the same: ask "add a field that shows how many days each animal has been in the shelter".

4. Errors

A cell shows an error code when a row cannot be computed; it passes through everything that uses it. Catch one with IFERROR(value, fallback).

#VALUE!A value of the wrong kind, e.g. text where a date is needed. Text is not a date: use DATETIME_PARSE({Text field}).
#DIV/0!Divided by zero.
#NUM!Not a number, e.g. SQRT(-1).
#NAME?An unknown function.
#REF!A field that does not exist (any more).
#CYCLE!The formula (indirectly) uses itself.
#ERROR!Anything else, e.g. ERROR() or a row that took too long.

5. All 108 functions

Names are case-insensitive; Excel and Baserow names work as aliases (AVG, CONCAT, …). Positions count from 1. "Gives" is the type of the result (any: it depends on the arguments).

Numbers

FunctionGivesWhat it doesExample
ABS(value)numberThe value without its signABS({Balance})
AVERAGE(value1, value2, ...)
also AVG
numberAverage of the values (blanks are skipped); also AVGAVERAGE({Q1}, {Q2}, {Q3})
CEILING(value, [significance])numberRounds up to the nearest multiple of significance (1 when left out)CEILING({Hours}, 0.25)
COUNT(value1, value2, ...)numberHow many of the values are numbersCOUNT({Q1}, {Q2}, {Q3})
COUNTA(value1, value2, ...)numberHow many of the values are not empty (the length of an array)COUNTA({Tags})
COUNTALL(value1, value2, ...)numberHow many values there are, empty ones includedCOUNTALL({Tags})
EVEN(value)numberRounds away from zero to the nearest even integerEVEN({Guests})
EXP(power)numbere raised to the powerEXP(1)
FLOOR(value, [significance])numberRounds down to the nearest multiple of significance (1 when left out)FLOOR({Price}, 5)
INT(value)numberThe largest integer not above the valueINT({Hours})
LOG(number, [base])numberLogarithm in a base (10 when left out)LOG(1000)
LN(number)numberNatural logarithmLN({Growth})
MAX(value1, value2, ...)anyThe largest value (the latest, for dates)MAX({Score}, 0)
MEDIAN(value1, value2, ...)numberThe middle valueMEDIAN({Q1}, {Q2}, {Q3})
MIN(value1, value2, ...)anyThe smallest value (the earliest, for dates)MIN({Price}, 100)
MOD(value, divisor)numberThe remainder after dividing (it has the sign of the divisor)MOD({Row}, 2)
ODD(value)numberRounds away from zero to the nearest odd integerODD({Seats})
POWER(base, power)numberbase raised to powerPOWER({Side}, 2)
ROUND(value, decimals)numberRounds to a number of decimals (0 to 10)ROUND({Amount} * 1.21, 2)
ROUNDDOWN(value, [decimals])numberRounds toward zero (negative decimals round to tens, hundreds, …)ROUNDDOWN({Price}, 1)
ROUNDUP(value, [decimals])numberRounds away from zeroROUNDUP({Hours}, 0)
SIGN(value)number1 for a positive value, -1 for a negative one, 0 for zeroSIGN({Balance})
SQRT(value)numberSquare rootSQRT({Area})
SUM(value1, value2, ...)numberAdds the values (and the values of arrays)SUM({Q1}, {Q2}, {Q3})
VALUE(text)numberReads text as a number ("€1,250.50" → 1250.5, "50%" → 0.5)VALUE({Amount as text})
SUMIF(range, criterion, [sum_range])numberAdds the values (of sum_range) where range meets the criterion (5, ">5", "<>x", "a*")SUMIF({Scores}, ">50")
COUNTIF(range, criterion)numberHow many values meet the criterionCOUNTIF({Tags}, "urgent")
AVERAGEIF(range, criterion, [average_range])numberAverage of the values (of average_range) where range meets the criterionAVERAGEIF({Scores}, ">0")

Text

FunctionGivesWhat it doesExample
CONCATENATE(text1, text2, ...)
also CONCAT
textJoins texts (same as &); also CONCATCONCATENATE({First}, " ", {Last})
ENCODE_URL_COMPONENT(text)textText made safe to put in a link"https://maps.example/?q=" & ENCODE_URL_COMPONENT({Address})
FIND(search, text, [start])numberPosition of search in text (1 is the first character), 0 when not there; case-sensitiveFIND("@", {Email})
LEFT(text, count)textThe first count charactersLEFT({Postcode}, 4)
LEN(text)numberNumber of charactersLEN({Name})
LOWER(text)textText in lower caseLOWER({Email})
MID(text, start, count)textcount characters from position start (1 is the first)MID({Code}, 3, 2)
REPLACE(text, start, count, replacement)textReplaces count characters from position startREPLACE({Phone}, 1, 1, "+31 ")
REPT(text, times)textThe text repeatedREPT("★", {Rating})
RIGHT(text, count)textThe last count charactersRIGHT({IBAN}, 4)
SUBSTITUTE(text, old, new, [index])textReplaces old by new everywhere, or only its index-th occurrenceSUBSTITUTE({Phone}, " ", "")
T(value)textThe value when it is text, else emptyT({Notes})
TRIM(text)textText without spaces at the start and endTRIM({Name})
UPPER(text)textText in capitalsUPPER({Code})
PROPER(text)textEvery word starting with a capitalPROPER({Name})
TEXTJOIN(separator, ignore_empty, text1, ...)textJoins the values (and arrays) with a separator, optionally skipping empty onesTEXTJOIN(", ", TRUE, {City}, {Country})
SPLIT(text, [separator])arraySplits text into an array (at commas when no separator is given)SPLIT({Keywords}, ";")
REGEX_MATCH(text, regex)booleanWhether the text matches the regular expression (RE2 syntax)REGEX_MATCH({Email}, "^[^@]+@[^@]+$")
REGEX_EXTRACT(text, regex)textThe first part of the text that matches; blank when nothing doesREGEX_EXTRACT({Notes}, "[0-9]{4}")
REGEX_REPLACE(text, regex, replacement)textReplaces every match ($1 is the first group)REGEX_REPLACE({Phone}, "[^0-9+]", "")

Logic

FunctionGivesWhat it doesExample
AND(value1, value2, ...)booleanTrue when every value is true (also &&)AND({Paid}, {Shipped})
OR(value1, value2, ...)booleanTrue when any value is true (also ||)OR({Urgent}, {Due} < 3)
XOR(value1, value2, ...)booleanTrue when an odd number of the values is trueXOR({A}, {B})
NOT(value)booleanThe opposite (also !)NOT({Archived})
IF(condition, then, [else])anyOne value when the condition is true, another when notIF({Stock} > 0, "In stock", "Sold out")
IFS(condition1, value1, ...)anyThe value of the first true conditionIFS({Score} >= 90, "A", {Score} >= 75, "B", TRUE, "C")
SWITCH(expression, pattern1, result1, ..., [default])anyThe result of the first pattern equal to the expression, else the defaultSWITCH({Status}, "Open", 1, "Done", 2, 0)
TRUE()booleanTrue (also written TRUE)TRUE()
FALSE()booleanFalse (also written FALSE)FALSE()
BLANK()anyAn empty valueIF({Done}, BLANK(), {Due})
ISBLANK(value)booleanTrue when the value is empty (blank, "" or an empty array)ISBLANK({Email})
ERROR([message])anyAn #ERROR! value, with an optional messageIF({Qty} < 0, ERROR("Negative quantity"), {Qty})
ISERROR(value)
also ISERR
booleanTrue when the value is an error (#DIV/0!, #VALUE!, …)ISERROR({Amount} / {Qty})
IFERROR(value, value_if_error)anyThe value, or value_if_error when it is an errorIFERROR({Amount} / {Qty}, 0)

Dates and times

FunctionGivesWhat it doesExample
DATETIME_PARSE(text, [format], [locale])dateReads text as a date and time; format uses moment tokens (DD/MM/YYYY HH:mm)DATETIME_PARSE({Date text}, "DD-MM-YYYY")
DATETIME_FORMAT(date, [format])textA date as text; format uses moment tokens (D MMMM YYYY, LL, h:mm A, …)DATETIME_FORMAT({Due}, "D MMM YYYY")
DATESTR(date)textThe date as YYYY-MM-DDDATESTR({Due})
TIMESTR(date)textThe time as HH:mm:ssTIMESTR({Starts})
YEAR(date)numberThe yearYEAR({Born})
MONTH(date)numberThe month, 1 to 12MONTH({Due})
DAY(date)numberThe day of the month, 1 to 31DAY({Due})
HOUR(date)numberThe hour, 0 to 23HOUR({Starts})
MINUTE(date)numberThe minute, 0 to 59MINUTE({Starts})
SECOND(date)numberThe second, 0 to 59SECOND({Starts})
WEEKDAY(date, [start])numberDay of the week: 0 is Sunday (or Monday with start "Monday") up to 6WEEKDAY({Due}, "Monday")
WEEKNUM(date, [start])numberWeek of the year (week 1 has January 1st; weeks start on Sunday, or Monday)WEEKNUM({Due})
DATEADD(date, count, unit)dateThe date plus count units (days, weeks, months, years, hours, …)DATEADD({Intake}, 14, "days")
DATETIME_DIFF(date1, date2, [unit])numberdate1 minus date2 in whole units (seconds when left out)DATETIME_DIFF(TODAY(), {Intake}, "days")
WORKDAY(start, days, [holidays])dateThe working day (Monday to Friday, not a holiday) days working days after startWORKDAY({Ordered}, 5, "2025-12-25, 2025-12-26")
WORKDAY_DIFF(start, end, [holidays])
also NETWORKDAYS
numberWorking days from start to end, both counted (negative when end is earlier)WORKDAY_DIFF({Start}, {End})
IS_BEFORE(date1, date2)booleanWhether date1 is before date2IS_BEFORE({Due}, TODAY())
IS_AFTER(date1, date2)booleanWhether date1 is after date2IS_AFTER({Delivered}, {Due})
IS_SAME(date1, date2, [unit])booleanWhether the dates are the same (the same day, month, … with a unit)IS_SAME({Due}, TODAY(), "day")
TODAY()dateToday in the formula’s timezone (updated daily)DATETIME_DIFF(TODAY(), {Intake}, "days")
NOW()dateThe current date and time (updated every 15 minutes)DATETIME_DIFF(NOW(), {Created}, "hours")
TONOW(date)numberDays from the date until now (positive for a past date)TONOW({Last contact})
FROMNOW(date)numberDays from now until the date (positive for a future date)FROMNOW({Due})
SET_TIMEZONE(date, timezone)dateThe date shown in another timezone ("Europe/Amsterdam")DATETIME_FORMAT(SET_TIMEZONE({Starts}, "America/New_York"), "LLL")
SET_LOCALE(date, locale)dateThe date shown in another language ("nl", "de", …) by DATETIME_FORMATDATETIME_FORMAT(SET_LOCALE({Due}, "nl"), "D MMMM YYYY")

Arrays (lists of values)

FunctionGivesWhat it doesExample
ARRAYJOIN(array, [separator])textThe items joined into text (", " when no separator is given)ARRAYJOIN({Tags}, " · ")
ARRAYUNIQUE(array)
also UNIQUE
arrayThe array without repeated itemsARRAYUNIQUE({Tags})
ARRAYCOMPACT(array)arrayThe array without empty itemsARRAYCOMPACT({Values})
ARRAYFLATTEN(array)arrayNested arrays turned into one flat arrayARRAYFLATTEN([{Tags}, {Labels}])
ARRAYSLICE(array, start, [end])arrayItems start to end (1 is the first item, -1 the last; end included)ARRAYSLICE({Tags}, 1, 3)
FILTER(array, condition)arrayThe items for which the condition is true; item is the element, index its position (1 is the first)FILTER({Tags}, item != "internal")
MAP(array, expression)arrayThe expression for every item; item is the element, index its position (1 is the first)MAP({Tags}, UPPER(item))
SORT(array, [key], [order])arrayThe items sorted; key is an expression of item, order "asc" (default) or "desc"SORT({Tags}, "desc")
REDUCE(array, initial, expression)anyCombines the items into one value: acc starts as initial and becomes the expression for each item; item is the element, index its position (1 is the first)REDUCE({Scores}, 0, acc + item)
FIRST(array)anyThe first itemFIRST({Tags})
LAST(array)anyThe last itemLAST({Tags})
INDEX(array, position)anyThe item at a position (1 is the first, -1 the last); blank past the endINDEX(SPLIT({Full name}, " "), 1)
CONTAINS(array, value)booleanWhether the array has the value as an item (or the text contains it)CONTAINS({Tags}, "urgent")
ANY(array, [condition])booleanWhether the condition is true for any item (any item is true, without a condition); item is the element, index its position (1 is the first)ANY({Scores}, item > 90)
ALL(array, [condition])booleanWhether the condition is true for every item; item is the element, index its position (1 is the first)ALL({Checks}, item = "OK")

The row itself

FunctionGivesWhat it doesExample
RECORD_ID()textThe id of the row"https://example.com/rows/" & RECORD_ID()
CREATED_TIME()dateWhen the row was addedDATETIME_DIFF(NOW(), CREATED_TIME(), "days")
LAST_MODIFIED_TIME([field1, ...])dateWhen the row last changed (of the whole row; fields are not tracked one by one)LAST_MODIFIED_TIME()
CREATED_BY()textWho added the rowCREATED_BY()
LAST_MODIFIED_BY()textWho last changed the rowLAST_MODIFIED_BY()

More: Linking lists (Rollup, Count, Lookup) · REST API reference · AI agents (MCP) · Formula guide for AI agents