IIf
Picks one of two values from a boolean expression. Runtime-proven on a live Marketing Cloud Engagement CloudPage — including the undocumented fact that only the selected branch is evaluated, and that a plain string condition always picks the false branch.
Syntax
IIf(expression, trueValue, falseValue) → string
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
expression |
boolean | Yes | Boolean expression to evaluate — a non-boolean value always selects the false branch |
trueValue |
string | number | boolean | date | Yes | Value returned when true; evaluated only when true is selected |
falseValue |
string | number | boolean | date | Yes | Value returned when false; evaluated only when false is selected |
Example
The condition has to be a real comparison or a boolean-returning function:
%%[
VAR @firstName
SET @firstName = AttributeValue("FirstName")
]%%
Hello %%=IIf(Empty(@firstName), "there", @firstName)=%%,
Passing the value itself does not work. This renders anonymous even when the name is present, because a plain string never selects the true branch:
%%=IIf(@firstName, @firstName, "anonymous")=%%
Because only the selected branch runs, an expensive or fragile call is safe in the branch that is not taken:
%%=IIf(Empty(@key), "no lookup needed", Lookup("Preferences", "Tier", "SubscriberKey", @key))=%%
Return value
string — whichever branch argument was selected, returned unconverted.
The domain is whatever the caller passes, so there is no set of literals to test for. The run rendered T, F, 42 and a full date-time string from this function.
Behaviour
A comparison selects correctly in both directions. 1 == 1 took the true branch, 1 == 2 the false one. A boolean-returning function works as the condition too, including negated with NOT.
There is no truthiness. Eight non-boolean conditions — an undeclared variable, a variable declared but never assigned, the empty string, three spaces, 0, "0", "false" and the plain string hello — every one selected the false branch. Since the references type the first parameter as a string, writing IIf(@name, ...) and expecting a non-empty name to be true is an easy mistake, and it fails silently.
Branch values pass through unconverted. A number came back as 42, a date as its full date-time string.
Only the selected branch is evaluated
This is the most useful undocumented property of the function, and it needed a control to prove.
A call that reliably aborts the page was parked in the branch that should not be taken — in both directions. Both requests returned HTTP 200 with the other branch’s value and both markers printed. On its own that only shows nothing bad happened; so the same deploy also called that aborting function directly in its own branch, and that request returned HTTP 422 with no output at all. The abort was reachable, and simply was never reached.
Neither reference says anything about evaluation order, so this is undocumented rather than contradicted. Practically it means a Lookup, a HTTPGet or any other costly call can sit in a branch guarded by the condition, and it will not run unless it is the answer.
How the four Utility tests compare
The same inputs put through all four functions, on one page, in one run:
| Input | Empty |
IsNull |
IsNullDefault(x, "DEF") |
IIf(x, "T", "F") |
|---|---|---|---|---|
| undeclared variable | True | False | (empty) | F |
| declared, never set | True | False | (empty) | F |
"" |
True | False | (empty) | F |
" " |
False | False | ` ` | F |
0 |
False | False | 0 |
F |
"0" |
False | False | 0 |
F |
"false" |
False | False | false |
F |
"hello" |
False | False | hello |
F |
The last column is constant, which is the point: feed this function a value and it always answers false. Feed it Empty(value) — the first column — and it answers the question that was actually meant.
Show test script
%%[
VAR @b, @unset, @es, @sp, @z, @z0, @sf, @val
SET @b = RequestParameter("b")
SET @es = ""
SET @sp = " "
SET @z = 0
SET @z0 = "0"
SET @sf = "false"
SET @val = "hello"
/* known-good control: renders on every request, so a run of HTTP 422s
can be told apart from a broken deploy */
OutputLine(Concat("CTRL=[", Uppercase("ok"), "]"))
/* comparisons and boolean-returning functions in the condition slot */
IF @b == "f_bool" THEN
OutputLine(Concat("--- f_bool start ---"))
OutputLine(Concat("F_EXPT=[", IIf(1 == 1, "T", "F"), "]"))
OutputLine(Concat("F_EXPF=[", IIf(1 == 2, "T", "F"), "]"))
OutputLine(Concat("F_FN=[", IIf(Empty(@es), "T", "F"), "]"))
OutputLine(Concat("F_NOT=[", IIf(NOT Empty(@es), "T", "F"), "]"))
OutputLine(Concat("--- f_bool done ---"))
ENDIF
/* a name that was never declared, in the condition slot */
IF @b == "f_und" THEN
OutputLine(Concat("--- f_und start ---"))
OutputLine(Concat("F_UND=[", IIf(@zzz_never_declared, "T", "F"), "]"))
OutputLine(Concat("--- f_und done ---"))
ENDIF
/* declared with VAR, never assigned */
IF @b == "f_unset" THEN
OutputLine(Concat("--- f_unset start ---"))
OutputLine(Concat("F_UNSET=[", IIf(@unset, "T", "F"), "]"))
OutputLine(Concat("--- f_unset done ---"))
ENDIF
/* the explicitly empty string */
IF @b == "f_es" THEN
OutputLine(Concat("--- f_es start ---"))
OutputLine(Concat("F_ES=[", IIf(@es, "T", "F"), "]"))
OutputLine(Concat("--- f_es done ---"))
ENDIF
/* three spaces - the input a truthy language would call true */
IF @b == "f_sp" THEN
OutputLine(Concat("--- f_sp start ---"))
OutputLine(Concat("F_SP=[", IIf(@sp, "T", "F"), "]"))
OutputLine(Concat("--- f_sp done ---"))
ENDIF
/* the number zero */
IF @b == "f_z" THEN
OutputLine(Concat("--- f_z start ---"))
OutputLine(Concat("F_Z=[", IIf(@z, "T", "F"), "]"))
OutputLine(Concat("--- f_z done ---"))
ENDIF
/* the string "0" */
IF @b == "f_z0" THEN
OutputLine(Concat("--- f_z0 start ---"))
OutputLine(Concat("F_Z0=[", IIf(@z0, "T", "F"), "]"))
OutputLine(Concat("--- f_z0 done ---"))
ENDIF
/* the string "false" */
IF @b == "f_sf" THEN
OutputLine(Concat("--- f_sf start ---"))
OutputLine(Concat("F_SF=[", IIf(@sf, "T", "F"), "]"))
OutputLine(Concat("--- f_sf done ---"))
ENDIF
/* a genuine non-empty string as the condition */
IF @b == "f_val" THEN
OutputLine(Concat("--- f_val start ---"))
OutputLine(Concat("F_VAL=[", IIf(@val, "T", "F"), "]"))
OutputLine(Concat("--- f_val done ---"))
ENDIF
/* non-string branch values: a number and a date */
IF @b == "f_type" THEN
OutputLine(Concat("--- f_type start ---"))
OutputLine(Concat("F_NUMBR=[", IIf(1 == 1, 42, 7), "]"))
OutputLine(Concat("F_DATEBR=[", IIf(1 == 2, "no", Now()), "]"))
OutputLine(Concat("--- f_type done ---"))
ENDIF
/* page-aborting call parked in the FALSE branch while TRUE is selected */
IF @b == "f_lazyf" THEN
OutputLine(Concat("--- f_lazyf start ---"))
OutputLine(Concat("F_LAZYF=[", IIf(1 == 1, "T", RaiseError("iif-false-branch-evaluated")), "]"))
OutputLine(Concat("--- f_lazyf done ---"))
ENDIF
/* the same probe in the other direction */
IF @b == "f_lazyt" THEN
OutputLine(Concat("--- f_lazyt start ---"))
OutputLine(Concat("F_LAZYT=[", IIf(1 == 2, RaiseError("iif-true-branch-evaluated"), "F"), "]"))
OutputLine(Concat("--- f_lazyt done ---"))
ENDIF
/* the control that makes the two branches above mean something: the same
aborting call, invoked directly. This branch must NOT render - expect
HTTP 422 with no start marker */
IF @b == "f_ctrl" THEN
OutputLine(Concat("--- f_ctrl start ---"))
OutputLine(Concat("F_CTRL=[", RaiseError("iif-control-direct-call"), "]"))
OutputLine(Concat("--- f_ctrl done ---"))
ENDIF
]%%
A wrong argument count aborts AMPscript at compile time, so it takes down every branch on the page — including the control block and branches that were never requested. Keep arity checks out of a gated behaviour harness and give each one its own deployment, or a whole run returns uninformative HTTP 422s.
Availability
| Platform | Available |
|---|---|
| Marketing Cloud Engagement | Yes |
| Marketing Cloud Next | Yes (since 67) |
See also
- Empty — the boolean this function is most often given
- IsNull — returns a boolean too, but
Falsefor everything a page variable holds - IsNullDefault — the fallback it looks like; pair
IIfwithEmptyinstead - Official reference · ampscript.guide