Calculated Field

Derive a value from other form fields with a small formula language — totals, taxes, durations, conditional labels, and table aggregates

What This Feature Does

A Calculated field doesn't ask the form filler for input — instead, the form works out its value from the answers already given in other fields. As the filler types, the Calculated field updates live.

Typical examples:

  • Line total = {quantity} * {unitPrice}
  • Tax = {subtotal} * 0.1
  • Status label = IF({age} >= 18, "Adult", "Minor")
  • Job duration = {endTime} - {startTime}
  • Subtotal over a table = SUM({lineItems.amount})

The value the form filler sees is the same value the server stores — every Calculated field is re-computed server-side at submit time, so the persisted scalar is always the formula's truth.

When to Use a Calculated Field

  • Line totals on a quote, invoice, or purchase order
  • Subtotals over a Table field (materials list, line items, daily timesheet rows)
  • Tax / GST applied to a subtotal
  • Derived labels ("Adult / Minor", "Pass / Fail", "On-track / Late")
  • Durations between two times or dates
  • Conditional discounts ("10% off if more than 5 items")
  • Any number the field crew would otherwise reach for a calculator to work out

If the value should come from outside the form (a record in another module, last week's submission, a database lookup), use a Resource field or a Workflow instead — Calculated fields only see data the same submission has captured.

Adding a Calculated Field

  1. Open your form in the Form Builder
  2. From the field palette, click the Calculator icon and drop it onto your form (or drag-and-drop like any other field)
  3. Give it a Label ("Subtotal", "Tax", "Total") and a Name (the internal identifier other formulas use to refer to it)
  4. Pick an Output format — Number, Currency, Duration, Text, or Yes/No (see Output formats)
  5. In the Formula box, use the Visual or Advanced builder to compose the expression
  6. Watch the Live preview under the formula — it shows what the formula will produce using each referenced field's default value
  7. Save the form

Visual vs Advanced Builder

The Calculated field has two authoring modes, toggled at the top of the formula builder.

ModeWhen it suitsWhat it looks like
VisualComposing a new formula on a desktop, learning the syntaxChip-based — pick a field, drop in an operator chip, pick a function, drop in a number. Drag chips to reorder.
AdvancedEditing an existing formula, working on a phone, copy-pasting a formula from a sibling formPlain-text editor with auto-paired brackets and field-name autocomplete.

On a phone, Advanced is the default — chip drag-and-drop on a 5-inch screen is too cramped. You can switch at any time without losing your work — both modes read from and write to the same formula string.

If you're in Visual mode and an Advanced-mode formula contains syntax the chips can't represent (e.g. a deeply nested conditional), Visual mode shows a "switch to Advanced to edit" notice rather than silently dropping the syntax.

Formula Syntax Basics

Formulas are written using friendly operator names and the {fieldName} reference syntax.

Arithmetic

  • + add — {a} + {b}
  • - subtract — {a} - {b}
  • * multiply — {quantity} * {unitPrice}
  • / divide — {total} / {count}

Comparison

In the chip builderIn Advanced modeMeans
EQ==equals
NEQ!=not equal
GT>greater than
GTE>=greater than or equal
LT<less than
LTE<=less than or equal

Logical

In the chip builderIn Advanced modeMeans
AND&&both conditions true
OR||either condition true
NOT!invert the condition

Conditional

Use the IF(condition, then, else) function:

IF({hoursWorked} > 8, "Overtime", "Regular")

Referencing other fields

Wrap the field's name (not its label) in braces:

The Advanced editor offers field-name autocomplete: type { and a dropdown appears with every field you can reference.

You cannot reference:

  • The Calculated field itself (no self-reference)
  • A Signature, File Upload, or Map field (those values aren't numbers)
  • A field that doesn't exist yet — save errors will flag it

Output Formats

The output format controls how the result is shown on screen and how it's stored.

FormatExample formulaExample resultNotes
Number{quantity} * {unitPrice}42.50Choose precision (0–10 decimal places).
Currency{subtotal} * 1.1$1,099.00Choose symbol (e.g. $, , £, A$) and position (prefix or suffix).
Duration({endTime} - {startTime})8h 30mThe formula must yield a number in seconds; Times are auto-converted (HH:MM → decimal hours, then displayed back).
TextIF({status} == "ok", "Pass", "Fail")PassThe only format where you can return a string from IF.
Yes/No{age} >= 18Yestrue / 1 → "Yes", false / 0 → "No".

Functions

Eight named functions are available. Names are UPPERCASE.

FunctionWhat it doesExample
SUM(...)Add every numeric valueSUM({lineItems.amount}) totals every row's amount
AVG(...)Average of every numeric valueAVG({ratings.score}) — average score
COUNT(...)Count of numeric, non-empty valuesCOUNT({lineItems.qty}) — how many rows have a qty
MIN(...)Smallest valueMIN({prices.amount})
MAX(...)Largest valueMAX({prices.amount})
ROUND(x, decimals)Round to N decimal placesROUND({subtotal} * 0.1, 2) — tax rounded to cents
ABS(x)Absolute value (drop the minus sign)ABS({delta})
IF(cond, then, else)Branch on a conditionIF({age} >= 18, "Adult", "Minor")

Aggregates (SUM/AVG/COUNT/MIN/MAX) accept either a list of arguments or a Table column:

SUM(1, 2, 3)                  → 6
SUM({lineItems.qty})          → sum of every row's qty
SUM({lineItems.qty}, 5)       → sum of every row's qty + 5

Empty aggregates return null for AVG/MIN/MAX (the field shows "—"), and 0 for SUM/COUNT.

Working with Table Fields

To pull a column out of a Table (or Material Transaction or Equipment Time) field, use dot syntax: {tableName.columnName}.

A Table called lineItems with columns qty, unitPrice, and description exposes:

  • {lineItems.qty} — the array of every row's qty
  • {lineItems.unitPrice} — the array of every row's unitPrice

Wrap a column reference in an aggregate to get a single number:

SUM({lineItems.qty} * {lineItems.unitPrice})    ← this does NOT work yet
SUM({lineItems.lineTotal})                      ← this DOES work
                                                   if lineTotal is a column

Per-cell formulas inside Table rows aren't supported yet. The workaround is to add a lineTotal column to the Table whose values the form filler enters (or, more usually, that another Calculated field outside the table sums).

Hidden / conditionally-hidden rows are skipped from aggregates. If a row has __rowVisible: false (set automatically by row-level conditional logic), it's not counted.

Conditional Visibility

A Calculated field can be hidden or shown based on other field values, just like any other field. Open the field's Conditional section in the configuration panel.

A hidden Calculated field still computes its value behind the scenes. That means:

  • Workflows that read the submission still see the value
  • Other Calculated fields that depend on it still get the right number
  • The PDF export decides whether to show it based on the same visibility rule

If you want a Calculated field to genuinely not compute (e.g. because referenced fields aren't filled in yet), make sure those referenced fields are themselves blank or hidden — the formula treats missing references as zero.

Pitfalls

  • Referencing a field that doesn't exist yet — the form will refuse to save with a red banner under the Calculated field. Fix: create the referenced field first, or correct the spelling. The field-name autocomplete in the Advanced editor helps avoid this.
  • Renaming a referenced field — the formula will keep the old name and stop working. Open the Calculated field and re-pick the renamed field (or hand-edit the formula).
  • Changing the output format on a published form — workflows downstream may be expecting the old type. A workflow that expected "$1,099.00" (Currency) won't recognise 1099 (Number).
  • Division by zero — the field shows "—" rather than crashing. If you want a friendlier fallback, wrap the formula in IF: IF({count} > 0, {total} / {count}, 0).
  • Cycles between Calculated fieldstotal = subtotal + tax and tax = total * 0.1 is a cycle. Save will refuse the form with a cycle error showing the chain (total → tax → total).
  • Self-reference — a Calculated field cannot reference its own name. The formula box will reject it.
  • Editing the formula does not retro-update old submissions. Each submission stores the formula as it was at submit time. A re-published form's new formula only applies to new submissions. (Behind the scenes, the PDF export of an old submission uses the snapshot too.)
  • Big numbers — formulas use standard JavaScript numbers. Currency totals above ~9 quadrillion will lose precision. Realistic invoices are fine.

For Developers / Advanced Users

The formula engine is a deliberately small expression language. Full developer documentation — threat model, the public API, how to add a new function, the lifecycle of a formula from edit to PDF — lives in the codebase:

packages/features/forms/src/form-builder/lib/formula/README.md

The dev-tool component catalog also has an interactive playground for the Calculated field's config drawer — search for Calculated Field in the dev-tool components page.

Frequently Asked Questions

Why doesn't total = {qty} * {price} work?

The = would be an assignment, which is disabled. A Calculated field's label is "Total"; the formula is just the right-hand side: {qty} * {price}.

How do I average just the visible rows of a Table?

Aggregates already skip hidden rows automatically. If you've added a row-level conditional to your Table, AVG({rows.col}) averages only the rows that condition shows.

Can I reference a Calculated field from another Calculated field?

Yes. The renderer and the server both walk Calculated fields in dependency order, so total = subtotal + tax and tax = subtotal * 0.1 work fine — subtotal is computed first, tax second, total last.

Why does my Duration field show "0s"?

Either the formula yielded 0 (start and end are the same), or it yielded a negative number — durations clamp to zero rather than show "−2h 30m". Check the order of your subtraction.

Can a Calculated field include text from another field?

Not by concatenation — there's no CONCAT function. The only string-producing operator is IF(cond, "yes", "no"). If you need fully dynamic text, use a Workflow with a template, not a Calculated field.

What happens if a referenced field is hidden?

The hidden field's value is treated as null — i.e. missing. In arithmetic, null propagates as null (so null + 5 is null, and the Calculated field shows "—"). In aggregates, null rows are skipped.

What's Next

  • Add the field to a form via the Form Builder
  • Combine with Conditional Logic to show derived values only when relevant
  • Trigger a Workflow when a Calculated value crosses a threshold
  • Reference Calculated values from the Template Builder to surface them on the generated PDF