---
title: "Calculated"
description: "Show a derived number computed from other answers, recomputed server-side on submit."
group: "Build forms"
order: 19
type: "reference"
parent: "fields"
keywords:
  - "calculated"
  - "calc"
  - "formula"
  - "total"
  - "derived"
  - "decimals"
  - "prefix"
  - "suffix"
updated: "2026-07-18"
---

The calculated field derives a number from other answers — order totals, scores, quantities — and stores it like any other answer. It renders as a read-only value, never an input: respondents watch it update live as they answer, and the server recomputes it from the validated answers on submit, so a tampered client value is ignored by construction.

## Options

| Option | Type | Default | Behavior |
| --- | --- | --- | --- |
| `calc` | `CalcExpr` | required | The typed expression tree to evaluate |
| `decimals` | `number` | none | Rounds the stored value (0–6, half-away-from-zero) |
| `prefix` | `string` | none | Display-only text before the value, e.g. `$` |
| `suffix` | `string` | none | Display-only text after the value, e.g. ` kg` |

A calculation may read `number`, `rating`, `linear_scale`, and other `calculated` fields. Evaluation is identical live and on submit because both run over the answers as they will be stored: strings are trimmed, an unchecked checkbox or otherwise empty answer reads as unanswered, and a source that is logic-hidden or on a jump-skipped page reads as unanswered — even if a value was typed there before the flow changed. Any unanswered operand (or a division by zero) makes the whole result unanswered — the key is simply absent from the response, piping renders empty, and the grid shows an empty cell. Calculated fields are never `required` and are never prefilled from the URL.

## The expression tree

`calc` is a typed AST, not a formula string:

```ts
type CalcExpr =
  | { op: "value"; fieldId: string }
  | { op: "const"; value: number }
  | { op: "add" | "mul" | "min" | "max"; args: CalcExpr[] }
  | { op: "sub" | "div"; left: CalcExpr; right: CalcExpr }
  | { op: "round"; arg: CalcExpr; decimals?: number }
  | { op: "if"; when: Condition[]; then: CalcExpr; else: CalcExpr };
```

`if.when` uses the same condition model as visibility rules and page jumps, so one condition language covers all three. Schema validation rejects a reference to a missing or non-numeric field and any reference cycle outright — a calculated field never silently degrades.

## Add a calculated field

In the dashboard, select **Add block**, choose **Calculated value**, then pick an operation (sum, difference, product, quotient, minimum, or maximum), the numeric fields it reads — from any page — and an optional constant term. Decimals, prefix, and suffix sit alongside. The editor compiles that to a one-level expression; fields that would create a reference cycle aren't offered.

Deeper expressions — nesting, `round`, `if` — are written in code. When a form defines one, the builder shows a read-only "Edited in code" notice instead of an editor that would flatten it.

In code:

```ts
const orderTotal = {
  id: "order_total",
  kind: "calculated",
  label: "Order total",
  calc: {
    op: "mul",
    args: [
      { op: "value", fieldId: "quantity" },
      { op: "const", value: 19.5 },
    ],
  },
  decimals: 2,
  prefix: "$",
} as const;
```

The computed value participates everywhere an answer does: pipe it with `{{order_total}}`, branch visibility or page jumps on it, and read it from the responses grid, CSV exports, and webhook payloads. Piping inserts the plain number; the display row, grid, and exports apply `decimals`, `prefix`, and `suffix` through the shared formatter.

## SDK compatibility

A form containing a calculated field serves a raised minimum SDK version (`0.11.0`) from the public form endpoint. Older embeds would render the form without the calculated row and mis-run any logic that reads it, so they fail fast with an update message instead.

## Related

- [Conditional logic](/docs/logic): Branch visibility and page flow on answers, including calculated ones.
- [Answer piping](/docs/logic#answer-piping): Render the computed value inside labels and text blocks.
