Result reference
The result block is the part people came for. It can say the same thing to everyone, or a different thing to each person, with their own numbers in it. This is everything it can do.
What a result is made of
One result block holds one or more versions. Exactly one is shown, picked by the rules below. Each version has a title, a summary, and optionally a row of figures — the small labelled bars. Above them sits the score, which you can rename or switch off. Below them sits whatever you ask for and whatever happens next, which is covered separately.
You can put a value into anything you type: a title, a summary, a figure’s label, a figure’s value, the name of the score. It is filled in for each person.
Putting answers in copy
Double braces. The editor has buttons that insert these for you, so you rarely have to type one:
Your {{ labels.goal }} plan costs around {{ computed.quote | currency }}.There are six things you can read from:
| Name | What it holds |
|---|---|
answers | The underlying value of each answer, such as answers.rooms or answers.email. For a multiple-choice question this is the option’s id, not its wording. |
labels | What the visitor actually read, such as “Three or four”. Use this in copy. Same field names as answers. |
score | Their score, 0 to 100. |
band | Which score band they landed in, if the funnel has any. |
computed | Anything the funnel worked out — see calculations. |
consts | Fixed figures the funnel carries: a base price, a rate table. |
Use labels in copy and answers in conditions. A result page that says “option-bdd6e9” is the sign you reached for the wrong one.
Formatting
A vertical bar passes the value through a formatter first. Currency and number formatting follow the funnel’s locale, so a UK funnel gets £ and commas without you asking.
| Filter | Turns | Into |
|---|---|---|
{{ computed.estimate | currency }} | 412000 | £412,000 |
{{ computed.estimate | currency('USD') }} | 412000 | $412,000 |
{{ computed.hours | number(1) }} | 12.25 | 12.3 |
{{ computed.share | percent }} | 0.42 | 42% |
{{ answers.email | urlencode }} | a b@x.com | a%20b%40x.com |
{{ labels.plan | upper }} | Starter | STARTER |
lower works the same way as upper. percent expects a fraction, so 0.42 rather than 42 — pass a whole number and you will be told your visitor is 4,200% of something.
Different results for different people
There are two ways to show someone a different result, and they can be mixed.
By score. In the editor, Say something different for a higher score adds a version and asks for a threshold. Someone scoring 74 gets the highest version whose threshold they clear. The version at the bottom, with no threshold, is what everyone else sees.
By condition. A version can carry a rule instead. It can be anything the expression language can express. Conditions are checked before score thresholds, and the first version whose rule is true wins.
Order matters, and it is the order they appear in the editor. Always leave the unconditional version last: it is the safety net, and a funnel where nobody matches shows the first version rather than nothing.
Conditions
A condition is built from three dropdowns: the question, how to compare it, and the answer. Write it myself shows you the same rule as text. Everything the dropdowns produce reads back into them, so the two are the same thing in different clothes.
The text form is what you need for rules the dropdowns cannot make. Some that do real work:
answers.timing == 'as-soon-as-possible'
answers.rooms >= 4 && answers.access == 'parking'
score > 70 || contains(answers.services, 'seo')
computed.estimate > 500000
answers.budget < 2000 && !answers.urgentComparison is forgiving on purpose. answers.rooms == "3" is true when the stored answer is the number 3. You should not have to know which of the two it was saved as.
A condition that refers to something which does not exist comes out false rather than breaking the page. That covers both a typo and a question the visitor skipped. It is also why a version that never appears is usually a misspelt field name. The names are shown on the buttons above the summary box.
Figures
The row of small bars under the summary. Each has a label, a value and a bar length from 0 to 100. The label and the value are ordinary copy, so they can carry answers and formatting:
Label Estimated range
Value {{ computed.rangeLow | currency }} – {{ computed.rangeHigh | currency }}
Bar 70The bar is a judgement, not a calculation. It is only how full the bar draws, and nothing checks it against the value. “Strong demand” has no number behind it, and that is fine; the bar is there to make a page of text readable at a glance. Three figures fit on one row on a phone.
A figure with an empty label or value is refused when you publish. An empty bar in front of a visitor reads as something that failed to load, rather than something nobody filled in.
Expression reference
The same small language runs conditions, calculations and the values inside {{ }}, so there is only one thing to learn. It is not JavaScript: there are no variables, no loops and no way to reach the page or the network. A funnel is a document, and a document you can share must never be able to run code on somebody else’s screen.
Operators
| Kind | Operators |
|---|---|
| Arithmetic | + - * / % |
| Comparison | == != < <= > >= |
| Logic | && || ! |
| Choice | test ? this : that |
| Reaching in | a.b and a['b'] |
Functions
| Function | What it does |
|---|---|
round(x, digits) | Rounds. Digits can be negative, which rounds to tens, hundreds or thousands. round(412481, -3) is 412000, which is how an estimate stops looking falsely precise. |
floor(x) ceil(x) abs(x) | Down, up, and drop the minus sign. |
min(…) max(…) sum(…) | Take several numbers, or one list. |
clamp(x, low, high) | Keeps a number inside a range. Useful for a bar length that must stay 0–100. |
len(x) | How many things were chosen, or how long a piece of text is. |
contains(list, item) | Whether a multi-select includes something. Also works on text. |
lookup(table, key, fallback) | Reads a value out of a table by name. A rate per service, a multiplier per size. This is the one most calculators are built on. |
coalesce(…) | The first value that is not empty. Good for “their name, or ‘there’”. |
number(x, fallback) | Turns an answer into a number, using the fallback when it is not one. |
lower(x) upper(x) | Case, inside an expression rather than as a filter. |
Expressions are capped at 400 characters. If you are close to that, the thing you want is a calculation feeding another calculation.
Calculations
A funnel can work things out before the result is shown, and each working is available as computed.something. A cleaning company pricing a job works out like this:
rate = lookup(consts.rates, answers.service, 18)
hours = number(answers.rooms, 3) * consts.hoursPerRoom
quote = round(rate * hours, 0)
deposit = round(quote * 0.25, 0)Each one can use the ones above it. That is how a long sum stays readable, and how you stay under the 400-character cap. The order matters: a line that reaches for one below it gets nothing back. The editor tells you when that happens.
They live under Calculations, below the blocks in the editor. A name and an expression each, with arrows to reorder them. The name has to be readable back as computed.thatName, so letters, numbers and underscores only.
One thing that catches people: labels is empty inside a calculation. Calculations run before the wording is resolved, so compare against answers here — answers.service == 'deep-clean', not the words the visitor read. In result copy, where labels does work, use it.
The fixed figures a sum reads live under Fixed figures, just above. Either a single number, such as consts.hoursPerRoom, or a table keyed by an answer, read with lookup(consts.rates, answers.service, 18). That last number is what to use when the answer is not in the table. It is what stops a new option quietly costing nothing.
Common mistakes
| What you see | Why |
|---|---|
The result says option-bdd6e9 | answers in copy where you wanted labels. |
| A blank where a value should be | The name does not exist. Check spelling against the buttons above the summary box — answer.rooms and answers.room both quietly produce nothing. |
| Everyone sees the same version | A condition that is never true, or score thresholds with no bands behind them. Preview it and watch the score as you answer. |
| A percentage in the thousands | percent wants a fraction. Divide by 100 first. |
| A number with far too many decimals | Reach for round. An estimate of £412,481.33 claims a precision you do not have; round(x, -3) is honest and reads better. |
The preview beside the editor runs the real thing on real answers. So you can check anything above by answering your own funnel and watching what the result says.