SurveyLoopr
Tips & Tricks
12 min read

Matrix and Grid Questions in XLSForm (table-list)

Mehrab Ali
Mehrab AliPublished November 11, 2026

To build a matrix (grid) question in XLSForm, wrap the questions in a group and write table-list in the appearance column of its begin_group row; every question inside must be a select_one or select_multiple of the same choice list. Each question becomes one row, and the choices become the column headings. For more control, use field-list on the group with label on a header row and list-nolabel on every question. Every example below compiles with pyxform and ODK Validate.

Download example form (.xlsx)All five 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 make a matrix question with table-list?

A community survey asks how satisfied the household is with five public services, all on the same 4-point scale. As separate questions that is five screens with the same four answers. As a grid it is one screen:

typenamelabelappearancerequired
begin_groupservicesHow satisfied is the household with each of these services?table-list
select_one satisfactionsat_waterWater supplyyes
select_one satisfactionsat_clinicHealth clinicyes
select_one satisfactionsat_schoolPrimary schoolyes
select_one satisfactionsat_roadRoadsyes
select_one satisfactionsat_powerElectricityyes
end_groupservices
surveychoicessettings
list_namenamelabel
satisfaction1Very satisfied
satisfaction2Satisfied
satisfaction3Dissatisfied
satisfaction4Very dissatisfied
surveychoicessettings
  • The group's label is shown above the grid, so put the question there and the item in each row.
  • Each row is a normal question: it has its own name, its own column in the data and its own required, relevant and constraint.
  • Every row must use the same list. With two lists in one table-list group, the form is rejected (see the mistakes table below).

table-list is a shortcut. When the form is converted, pyxform turns it into a field-list group, a note with the group's label, a header row with the label appearance and list-nolabel on each question. The note and the header row each add an empty column to the data (named generated_table_list_label_... and reserved_name_for_field_list_labels_...); you can drop them at analysis time.

How do I build the grid by hand with label and list-nolabel?

Building the grid yourself gives you what table-list cannot: a note or instruction above the grid, and a header row repeated partway down a long table so the enumerator never loses track of the columns. A child feeding module asking about 24-hour food groups:

typenamelabelappearancerequired
begin_groupfood_groupsFood groups eaten yesterdayfield-list
notefg_introAsk about everything the child ate or drank yesterday, during the day or night.
select_one yes_no_dkfg_header_1Food grouplabel
select_one yes_no_dkfg_grainsGrains, roots and tuberslist-nolabelyes
select_one yes_no_dkfg_pulsesBeans, peas, lentils and nutslist-nolabelyes
select_one yes_no_dkfg_dairyMilk, yoghurt and cheeselist-nolabelyes
select_one yes_no_dkfg_meatMeat, fish and poultrylist-nolabelyes
select_one yes_no_dkfg_header_2Food grouplabel
select_one yes_no_dkfg_eggsEggslist-nolabelyes
select_one yes_no_dkfg_vitaVitamin A rich fruits and vegetableslist-nolabelyes
select_one yes_no_dkfg_other_fvOther fruits and vegetableslist-nolabelyes
end_groupfood_groups
surveychoicessettings
list_namenamelabel
yes_no_dk1Yes
yes_no_dk0No
yes_no_dk-99Don't know
surveychoicessettings
  • field-list puts the whole group on one screen. Without it, each row is its own screen and there is no grid.
  • label shows only the choice labels, with no buttons: that is the header row. Its question label ("Food group") sits in the first column.
  • list-nolabel shows only the buttons, lined up under the header.
  • Header rows are questions too, so each needs its own name (fg_header_1, fg_header_2). Leave them without required: nobody can answer them.

ODK also has a list appearance, which shows the choice labels next to the buttons on every row. Use it instead of a header row when the choice labels are long and the columns would not line up.

Can a matrix use select_multiple?

Yes. A row of checkboxes works the same way, with each question as a row. A food security survey asking which meals each group in the household skipped:

typenamelabelappearance
begin_groupmeals_skippedWhich meals did each of these people skip yesterday?table-list
select_multiple mealskip_childChildren under 5
select_multiple mealskip_womenAdult women
select_multiple mealskip_menAdult men
end_groupmeals_skipped
surveychoicessettings
list_namenamelabel
meal1Breakfast
meal2Lunch
meal3Dinner
surveychoicessettings

Each row is saved as a space-separated list of codes, such as 1 3. To make "None" exclusive in a row, add a constraint to that row; the none of the above guide has the expression.

How do I add skip logic inside a matrix?

relevant works on each row and on the whole group. A row that is not relevant disappears from the grid; a group that is not relevant hides the whole grid:

typenamelabelappearancerelevantrequired
integerschool_childrenHow many children in the household go to school?yes
select_one yes_nouses_clinicHas anyone in the household used the health clinic in the last 12 months?yes
begin_groupschool_ratingHow would you rate the school on each of these?table-list${school_children} > 0
select_one ratingsch_teachingQuality of teachingyes
select_one ratingsch_toiletsToiletsyes
select_one ratingsch_mealsSchool mealsyes
select_one ratingsch_clinic_linkReferrals to the health clinic${uses_clinic} = 1yes
end_groupschool_rating
surveychoicessettings
list_namenamelabel
rating1Good
rating2Fair
rating3Poor
surveychoicessettings
list_namenamelabel
yes_no1Yes
yes_no0No
surveychoicessettings

Put the questions that decide the logic before the grid, on their own screen. In a field-list, rows react as soon as an earlier question on the same screen is answered, which can make the grid jump while the enumerator is filling it in. For the expressions themselves, see the XLSForm skip logic guide.

Warning:

A grid shows all its rows at once, so required only complains when the enumerator tries to leave the screen. Mark every row required (as above) or a missed row is easy to swipe past.

Can a matrix have number or text boxes?

Not in ODK. table-list, label and list-nolabel only line up select questions; there is no grid of number or text inputs in ODK Collect, and that is a long-standing request on the ODK forum. The closest is a field-list group of short-labelled questions, all on one screen. A livestock count:

typenamelabelappearanceconstraintconstraint_message
begin_grouplivestockHow many of these animals does the household own now?field-list
integern_cattleCattle. >= 0 and . <= 500Enter a number from 0 to 500
integern_goatsGoats and sheep. >= 0 and . <= 500Enter a number from 0 to 500
integern_poultryChickens and ducks. >= 0 and . <= 2000Enter a number from 0 to 2000
end_grouplivestock
surveychoicessettings

For the same number of things measured several times (crops by area, harvest and sales), a repeat with one round per item is usually cleaner; see XLSForm repeat groups.

KoboToolbox's form builder has its own "Question Matrix" type, which can hold number and text columns. It is saved in the XLSForm as begin_kobomatrix with a kobo--matrix_list column, works only on KoboToolbox, and the form will not convert with standard pyxform or load in ODK Central.

How do I keep a matrix readable on a phone?

A grid that looks fine in Excel can be unreadable on a 6-inch screen. The ODK forum has years of threads about headers that do not line up with the buttons. What works in the field:

  • Four or five columns at most. A 5-point scale plus "Don't know" is already tight on a phone.
  • Short choice labels. "Very satisfied" fits; "Somewhat satisfied with the service" wraps and pushes the columns out of line. Put the full wording in the group label if needed.
  • About eight rows per grid. Split a 20-item battery into three groups, or repeat the header row as in the hand-built example.
  • Test on the actual devices. Tablets show grids well; small phones may need landscape mode.
Note:

Grids are fast but they invite straight-lining: the same answer down the whole column. For attitude scales where that matters, consider asking the items one at a time, or randomizing their order (see randomize choices and question order).

Common matrix mistakes and how to fix them

These are the messages pyxform and ODK Validate return, which is what ODK Central, KoboToolbox or the XLSForm converter shows when it rejects the form:

You wroteWrite insteadError you will see
select_one satisfaction and select_one yes_no in one table-list groupone list for every row, or two groupsBadly formatted table list, list names don't match: satisfaction vs. yes_no
begin_group with table-list but no end_groupclose the group with end_groupUnmatched 'begin_group'. No matching 'end_group' was found for the name 'services'.
table-list in the appearance of a questionput it on the begin_group rowNo error, but no grid: the question shows on its own
label and list-nolabel rows in a group without field-listfield-list on the begin_group rowNo error, but each row is a separate screen
list-nolabel rows with no label header rowadd a header question with the label appearanceNo error, but the buttons have no column headings
A text or integer question in a table-list groupmove it out of the group, or use a field-list groupNo error, but it is not a grid row

For other messages, see common XLSForm errors or paste the form into the free XLSForm validator.

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

The XLSForm syntax is the same on all three; what differs is how each app draws the grid:

Platformtable-list on a groupfield-list + label + list-nolabelNumber or text grid
ODK CollectYesYesNo
KoboToolbox (XLSForm upload)Yes, same syntaxYesOnly with Kobo's own Question Matrix (begin_kobomatrix)
SurveyCTOSee SurveyCTO's documentationYes, field-list group with label and list-nolabelNo

Web forms (Enketo, used by KoboToolbox and ODK Central) draw grids differently from the Android app, and the header row can wrap differently. Preview the form on the web and on a phone before fieldwork.

When a grid is the right choice

Use a grid when several items share one short answer scale and the enumerator reads them in a row: service satisfaction, yes/no food groups, symptom checklists, asset ownership. Keep separate questions when the items need different answer lists, follow-up questions or long explanations; a grid hides those details. For the other ways to change how questions look, see the XLSForm appearance column.

Tip:

You can describe the grid in plain language instead: "ask satisfaction with water, clinic, school, roads and electricity on a 4-point scale, as one table". LooprAI writes the group, the appearance and the choice list, then compiles the form with pyxform before anything is saved.

Build my first form free

Next: XLSForm appearance column →

Frequently Asked Questions

How do I create a matrix question in XLSForm?

Put the questions in a group and write table-list in the appearance column of the begin_group row. Every question inside must be a select_one or select_multiple of the same choice list. Each question becomes a row and the choices become the column headings.

What is the difference between table-list and field-list?

field-list shows every question in a group on one screen. table-list does that and also lines the questions up as a grid with one header row. pyxform converts table-list into a field-list group with a label header row and list-nolabel on each question.

Why do I get 'Badly formatted table list, list names don't match'?

The questions in a table-list group use different choice lists. A grid has one set of columns, so every row must use the same list. Change the rows to one list, or split them into two groups with their own table-list.

Can I put integer or text questions in an ODK matrix?

No. ODK lines up only select questions in a grid. For numbers, use a field-list group of short-labelled integer questions on one screen, or a repeat with one round per item. KoboToolbox's own Question Matrix allows number columns but works only on KoboToolbox.

How do I repeat the header row in a long matrix?

Build the grid by hand: field-list on the group, then a select_one with the label appearance wherever you want a header, and list-nolabel on each question row. Give every header row its own name and leave it optional.

Why are the matrix headings not lined up with the buttons?

Long choice labels or too many columns for the screen width. Shorten the choice labels, keep to four or five columns, try landscape mode, or use the list appearance so each row shows its own labels instead of a shared header.

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.