The Form Layout
How an XLSForm Is Laid Out
An XLSForm is a spreadsheet with three sheets that KoboToolbox reads as a form. You can build the same form in the web builder; the spreadsheet is faster for long forms and easier to review line by line. For a choice between Kobo and other tools, see KoboToolbox vs ODK vs SurveyCTO. All three read XLSForm.
| Sheet | What it holds | Key columns |
|---|---|---|
| survey | One row per question, note or group | type, name, label, required, relevant, constraint |
| choices | The answer options for select questions | list_name, name, label |
| settings | The form title and ID | form_title, form_id, version |
Two rules prevent most upload errors. Names (not labels) must have no spaces and must start with a letter or underscore. And a select question's type names its list: select_one yes_no uses the choices with list_name yes_no.
Write the questions first. Question wording, order and length are covered in how to design a questionnaire; this page is about wiring the logic.
Skip Logic and Limits
Skip Logic With the relevant Column
Skip logic hides a question unless a condition is true. In the Kobo builder you pick the controlling question from a menu. In an XLSForm you write the condition in the relevant column, referring to other questions as ${name}.
| Goal | relevant expression |
|---|---|
| Ask only if a prior answer was yes | ${attended} = 'yes' |
| Ask only for a number range | ${age} >= 15 |
| Ask if a multiple-choice option was picked | selected(${crops}, 'other') |
| Ask if a multiple-choice option was not picked | not(selected(${crops}, 'none')) |
| Ask only if two things are true | ${consent} = 'yes' and ${age} >= 18 |
Put a condition on a whole group by writing it on the begin_group row. Everything inside the group follows. This keeps the sheet short: one condition on the group instead of ten copies.
Two cautions. Hidden questions are saved as blank, so a hidden required question does not block submission. And an answer given before you changed it can disappear when the condition turns false, so ask the controlling question early in the form.
Constraints and Messages
A constraint is a rule the answer must pass before the interviewer can move on. In the constraint column the dot means the answer just entered.
| Rule | constraint expression |
|---|---|
| Age between 15 and 99 | . >= 15 and . <= 99 |
| Code in a fixed format (two letters, three digits) | regex(., '^[A-Z]{2}[0-9]{3}$') |
| "None" cannot be combined with other choices | not(selected(., 'none') and count-selected(.) > 1) |
| No more than five selections | count-selected(.) <= 5 |
Always fill constraint_message with plain words that tell the interviewer what to fix: "Age must be between 15 and 99." A silent block is the main reason enumerators work around constraints.
Constraints are not checked on blank answers. Add yes in the required column if the answer is mandatory. Use hard limits for impossible values and a soft warning (a note, or a follow-up confirm question) for unusual ones, so real outliers are not forced out of the data. This is the cheapest data quality control you have; see data quality assurance for the checks that run after collection.
Choice Lists
Choice Lists and Choice Filters
Each list lives once on the choices sheet, keyed by list_name, and any number of questions can reuse it. A yes and no list written once serves every yes or no question in the form.
A choice filter shows only the options that fit an earlier answer. The usual case is district then village:
- On the choices sheet, add a column (here
district) and fill it on each village row with the district it belongs to. - On the village question, put
district = ${district}in thechoice_filtercolumn. The left side is the column; the right side is the earlier question. - Ask the district question before the village question.
Keep option names short and stable (n_a, maize), and keep labels readable. If you change a name after data collection starts, old and new records stop matching.
Example and Testing
A Practice Form
A good practice form is a short follow-up survey, for example after a garden training. It uses a consent gate, a group with a condition, a code with a format rule, an age limit, a district and village filter, a multiple-choice question with an exclusive "None," and a calculated yield.
To try it: write the form in a spreadsheet using the columns above, then in KoboToolbox choose New, then Upload an XLSForm file, and deploy as a draft. Add a read_me sheet that lists what each column does. Replace the practice questions with your own, keeping the logic columns.
Testing Before the Field
Run each rule with a wrong answer on purpose:
- Enter 5 for age, and abc for the code. Each should be blocked with its message.
- Choose "None" and "Maize" together. The form should refuse.
- Answer "No" to the training question. The sessions question should vanish.
- Choose North District, then check that only its villages appear.
Then test on a phone or tablet, not just in the browser preview, with a colleague who did not write the form. Follow with a small pilot; the difference between a pretest and a pilot is in questionnaire pretest vs pilot. For when digital collection pays off at all, see paper vs digital data collection.
Common Mistakes
Too many hard constraints. An age limit is fine. Forcing a plausible-looking income range will produce invented answers.
No constraint message. Enumerators cannot fix what they cannot read.
Skip logic on the label instead of the name. The expression uses the name column, written as ${name}. Renaming a question without updating the conditions breaks the form.
Changing choice names mid-survey. Edit labels freely; leave names alone once data is coming in.
Skipping the dead-end check. Walk every route through the form. A condition that cannot be true hides a question for everyone, and nobody notices until analysis.