SurveyLoopr
Tips & Tricks
12 min read

XLSForm Default Values: Static, Today and Dynamic

Mehrab Ali
Mehrab AliPublished November 12, 2026

An XLSForm default value goes in the default column: type the answer the question should start with, such as 1 for a choice named 1 or today() for a date. ODK Collect, KoboToolbox and SurveyCTO show it pre-filled, and the enumerator can change it. A default that depends on an earlier answer is different: it needs the calculation and trigger columns, because the default column is read when the form opens, before anything has been answered.

Download example form (.xlsx)All seven examples in one XLSForm, one group each. Tested with pyxform and ODK Validate; free to reuse under CC BY 4.0.Download

How do I set a default value in XLSForm?

Add a default column to the survey sheet and type the value as you would store it:

typenamelabeldefault
textcountryCountry of the surveyBangladesh
integervisit_numberVisit number1
dateinterview_dateDate of the interview2026-11-12
select_one yes_noconsentDoes the respondent agree to take part?1
select_multiple water_sourcewater_sourcesWhich water sources does the household use?1 2
decimalrice_pricePrice of rice per kg (BDT)51.5
surveychoicessettings
list_namenamelabel
yes_no1Yes
yes_no0No
water_source1Piped water
water_source2Protected well
water_source3Surface water
surveychoicessettings

Four rules cover most cases:

  • Choices use the name, not the label. 1 selects "Yes". Typing Yes compiles but selects nothing.
  • select_multiple takes several names separated by spaces: 1 2 ticks piped water and protected well.
  • Dates are YYYY-MM-DD. The date shows in the phone's own format, but the default is always written as 2026-11-12.
  • No quotes. The text is taken literally, so Bangladesh, not 'Bangladesh'.

A default is a starting point, not a fixed value. If the answer must never change, use a calculate row instead, or add read_only set to yes.

How do I default a date to today or the current time?

Write today() in the default column of a date question, or now() on a dateTime or time question:

typenamelabeldefault
datevisit_dateDate of the visittoday()
dateTimearrival_timeWhen did you arrive at the household?now()
timestart_clockWhat time did the interview start?now()
surveychoicessettings

The value is set once, when the form is first opened, and stays editable. That suits a visit date the enumerator may backdate after a day without signal. If you only need to record when the interview happened, without a question, use the today, start and end metadata types instead: they are filled automatically and cannot be edited.

How do I set a default from an earlier answer?

Put the expression in the calculation column of the visible question and the earlier question in the trigger column. This bed net question starts with the household size, and the enumerator lowers it when fewer people slept under a net:

typenamelabelcalculationtrigger
integerhh_sizeHow many people live in this household?
integerslept_under_netHow many of them slept under a mosquito net last night?${hh_size}${hh_size}
surveychoicessettings

Each time hh_size changes, the calculation runs again and fills slept_under_net. If the enumerator then types 4, the 4 stays, until hh_size is changed again. The question used as a trigger must have a label: a calculate row is refused (see the table below).

A trigger can also list several questions, separated by commas. The calculation runs when any of them changes. coalesce() keeps the total working while one count is still empty:

typenamelabelcalculationtrigger
integeradultsHow many adults (18 or older) live here?
integerchildrenHow many children (under 18) live here?
integerhh_sizeHousehold size (adults plus children, change if needed)coalesce(${adults}, 0) + coalesce(${children}, 0)${adults},${children}
surveychoicessettings

A list of triggers needs ODK Collect v2025.4 or later. A single trigger works from Collect v1.24.

Warning:

${hh_size} in the default column compiles without any error, and the question still opens empty. The default was calculated when the form opened, when hh_size had no answer yet. Move the expression to calculation and add trigger.

Why not just use the calculation column?

A calculation on a visible question without a trigger is recalculated every time the form changes and again when it is saved. The enumerator can type over it, but the calculated value comes back, and that is what the server receives. The trigger column changes this: the calculation only runs when the trigger question changes, so the enumerator's edit is kept.

Some older forms wrap the expression in once() for the same effect, as in unique IDs. once() stops after the first value, so if the earlier answer is corrected, the default does not follow. Use trigger for defaults.

How do I reuse the answer from the last saved form?

Write ${last-saved#question_name} in the default column. Each new form then starts with the value from the last form saved on that phone, which saves typing on details that rarely change during a day of fieldwork:

typenamelabeldefault
textvillageName of the village${last-saved#village}
textenumerator_nameYour name${last-saved#enumerator_name}
surveychoicessettings

The very first form on a device starts empty, since nothing has been saved yet. Only typed values are copied, never photos or other files. Last-saved needs ODK Collect v1.21 or later.

Note:

ODK stores last-saved values on the device unencrypted, even when the form uses encryption. Use it for a village or an enumerator's name, never for a respondent's name, phone number or health answers.

How do I copy the answer from the previous repeat?

Inside a repeat, a default is calculated each time a new instance is added. This expression takes the same question's answer from the instance just before it:

typenamelabeldefault
begin_repeatplotPlot
textmain_cropWhat is the main crop on this plot?${plot}[position() = position(current()/..) - 1]/main_crop
end_repeatplot
surveychoicessettings

Replace plot with your repeat's name and main_crop with the question's name, in both places. The first plot starts empty, and every next plot starts with the crop of the one before. When the question sits in a group inside the repeat, add one /.. per group and put the group in the path, as /g_details/main_crop. To read a repeat's answers from outside it, see indexed-repeat().

How do I clear an answer when an earlier one changes?

Use an empty calculation with a trigger. Here the notes about the water source are cleared whenever the enumerator picks a different source, so notes about a well never end up next to piped water:

typenamelabelcalculationtrigger
select_one water_sourcemain_sourceWhat is the household's main source of drinking water?
textsource_notesNotes on the main source''${main_source}
surveychoicessettings
list_namenamelabel
water_source1Piped water
water_source2Protected well
water_source3Surface water
surveychoicessettings

Common default value mistakes and how to fix them

These are the results from pyxform and ODK Validate, the same checks ODK Central and KoboToolbox run on upload:

You wroteWrite insteadWhat you will see
${hh_size} in default${hh_size} in calculation and in triggerNo error, but the question opens empty
Yes as the default of a select1 (the choice name)No error, but nothing is selected
TODAY()today()cannot handle function 'TODAY'
A calculate row in triggerA question with a labelThe question ${hh_calc} is not user-visible so it can't be used as a calculation trigger
A misspelled name in triggerThe exact namethe 'trigger' value is invalid. Reference variables must contain a name from the 'survey' sheet
A calculation on a visible question, no triggerAdd the trigger columnNo error, but the enumerator's edit is overwritten

The first two rows are the ones to watch for, because nothing tells you about them. For other messages, see common XLSForm errors, or paste your form into the free XLSForm validator.

Does this work the same in ODK, KoboToolbox and SurveyCTO?

The default column with fixed values and today() works the same on all three. The other features come from ODK's side of the XLSForm standard:

PlatformWhere defaults goNotes
ODK (XLSForm)default column; calculation plus trigger for earlier answerstrigger needs Collect v1.24 or later, last-saved v1.21 or later
KoboToolbox FormbuilderDefault response box in the question's settingsFixed values; for trigger and last-saved, upload an XLSForm
KoboToolbox (XLSForm upload)Same as ODKTest triggers in both Collect and the web form
SurveyCTOdefault columnExpressions in default are calculated the first time the field is shown, then left to the enumerator

Because SurveyCTO calculates a default when the field is first shown rather than when the form opens, ${hh_size} in its default column does pick up an earlier answer. The same form in ODK opens that question empty. trigger and ${last-saved#...} are defined by ODK: test them in SurveyCTO's form tester before you rely on them there.

When a default helps and when it hurts

A good default removes typing for an answer that is almost always the same: the country, today's date, the village for the rest of the day. A careless default creates answers nobody gave. If "Yes" is pre-selected on a consent question, a rushed enumerator can swipe past it, and the data shows consent that was never asked for. Keep defaults off consent, eligibility and any question the respondent must actually answer. For the rest, a default in the form beats a fill-down in Excel after fieldwork, because the enumerator sees the value and can correct it on the spot. The XLSForm calculations guide has more patterns for values the form works out for you.

Tip:

You can describe it in plain language instead: "pre-fill the bed net count with the household size, and start the village with the one from the last interview". LooprAI writes the default, calculation and trigger columns, then compiles the form with pyxform before anything is saved.

Build my first form free

Next: XLSForm calculation examples →

Frequently Asked Questions

How do I set a default value in XLSForm?

Add a column named default to the survey sheet and type the starting value in the question's row: a choice name such as 1, a date as 2026-11-12, or plain text without quotes. The question opens pre-filled and the enumerator can still change it.

How do I make a date question default to today?

Write today() in the default column of the date question, or now() for a dateTime or time question. The value is set once when the form opens and stays editable. To record the date without asking, use the today metadata type instead.

Why does my default ${field} not show the earlier answer?

ODK calculates the default column when the form opens, before the earlier question has an answer, so the question stays empty. Put the expression in the calculation column and the same field in the trigger column. It then fills when the earlier answer changes.

How do I set the default for a select_multiple question?

Type the choice names separated by spaces in the default column, for example 1 2 to tick the choices named 1 and 2. Use names from the choices sheet, not labels. A label such as Yes compiles without error but selects nothing.

How do I reuse the answer from the previous form?

Write ${last-saved#village} in the default column of the village question. Each new form starts with the value from the last form saved on that device. The first form is empty, and values are stored unencrypted, so avoid it for personal data.

Does the default column work the same in KoboToolbox and SurveyCTO?

Fixed values and today() work the same on all three. Kobo's Formbuilder calls it Default response. SurveyCTO calculates expressions in default when the field is first shown, so it can use earlier answers; in ODK and Kobo use calculation with trigger.

No Credit Card Required

Build the form first. Add hosting when you need it.

Start free with LooprAI, then deploy a managed ODK Central server or run DataSnap checks when your project is ready.

Automation handles the mechanical work.

Researchers decide what the evidence means.