Search the knowledge base, browse our resources, and visit our Community Forum for more detailed information
Last updated: 31 Aug 2026
Dynamic linking allows you to use data from a parent project within child projects, simplifying the management of longitudinal data collection. This article explains how to dynamically link data between KoboToolbox projects.
Note: Dynamic data attachments function similarly to the pulldata() function but eliminate the need for separate CSV files, since the data from a linked parent project serves as the data source.
You can retrieve various non-media responses from a parent project and perform calculations on this linked data in a child project. This can be useful for retrieving baseline data, contact information, or health records in cohort studies, or for confirming or verifying previously collected data.
We recommend using XLSForm to set up dynamic data attachments. For examples of parent and child projects, download sample files here and here. To learn how to set up dynamic data attachments in the KoboToolbox Formbuilder, see Dynamic data attachments using the Formbuilder.
Dynamically linking projects requires a parent project and at least one child project. The parent project requires no modification from a normal XLSForm. However, setting up the child project(s) involves the following steps:
In the survey worksheet of your XLSForm, add a row and set the question type to xml-external.
In the name column, provide a short name for the parent form. This name can consist of Latin alphabet characters, underscores, and numbers.
survey worksheet
type |
name |
label |
|---|---|---|
xml-external |
parent |
|
survey |
Throughout the form, you can retrieve values from the parent project by creating a new question and including the appropriate expression in the calculation column (see table below). You can use the following question types to retrieve data:
Use a calculate question type to retrieve and store values for future use within the form or dataset (e.g., for calculations or dynamic question labels).
Use text, integer, decimal, date, select_one, or select_multiple question types to include retrieved values as default responses in editable fields. Data edited in the child project will not change the original data in the parent project.
survey worksheet
type |
name |
label |
calculation |
|---|---|---|---|
xml-external |
parent |
||
text |
participant_id |
What is the participant’s ID? |
|
integer |
age |
Confirm the participant’s age |
instance(‘parent’)/root/data[enrollment_id = current()/../participant_id]/age |
survey |
Note:
To display linked data without allowing users to edit the field, use a calculate question followed by a note question that displays the calculated value. Alternatively, use text, integer, decimal, select_one, or select_multiple questions with the read_only column set to TRUE.
In the calculation column of the row where linked data will be retrieved, include one of the expressions in the table below. These expressions are called XPaths.
For each expression in the table below:
parent is the unique name assigned to the parent form (e.g., in the xml-external question of the child form).
parent_question refers to the name of a question from the parent form.
child_question refers to the name of a question from the child form.
parent_index_question is the identifying question from the parent form that links it to the child form (e.g., unique ID, organization name).
child_index_question is the identifying question from the child form that links it to the parent form (e.g., unique ID, organization name).
parent_group refers to the name of the group in the parent form in which the parent_question is located.
parent_index_group refers to the name of the group in the parent form in which the parent_index_question is located.
XPath |
Description |
|---|---|
|
Returns the total number of rows in the parent project. |
|
Returns the total number of rows in the parent project where |
|
Returns the total count of instances where the value of |
|
Returns the value of |
|
Same as above, but specifies that only data from the first instance of |
|
Returns the sum of values from |
|
Returns the maximum value entered in |
|
Returns the minimum value entered in |
Note: If the parent question is not included in any group, omit parent_group from the expression
Once your XLSForms are set up, log into your KoboToolbox account and follow these steps:
Create and deploy the parent project, if not already deployed.
Enable data sharing for the parent project:
In the SETTINGS > Connect Projects tab of the parent project, toggle the Data sharing switch (disabled by default) and click ACKNOWLEDGE AND CONTINUE in the confirmation window.
All data is shared by default, but you can restrict specific variables to share with child projects by clicking “Select specific questions to share”.
Note: If projects have different owners, the parent project owner must share the project with the child project owner. The minimum permissions required for dynamic data attachments to work are View form and View submissions. Note that this allows child project administrators to view all parent project data.
Create and deploy the child project.
Connect the child project to the parent project:
In the SETTINGS > Connect Projects tab of the child project, click the “Select a different project to import data from.” A dropdown menu will allow you to select a parent project to link.
Rename the linked parent project to the xml-external question name defined in the XLSForm and click IMPORT.
You can then select specific questions from the parent project to share with the child project (recommended), or select all questions.
If you add new fields to the parent form and wish to use them in the child project, re-import the parent project in the child project settings.
Note: Forms can only be linked together if they are on the same KoboToolbox server.
In some cases, the parent and child project need to be the same project. The setup process is the same as described above, except that you connect the project to itself.
A common use case is to count existing submissions for a specific index value. For example, you can use the submission count to prevent duplicate enrollment or track the number of follow-up visits for a participant.
You can also retrieve specific values from previous submissions in the same project. However, this requires additional planning when building the form to ensure that the correct submission is retrieved and to avoid errors.
You can link a project to itself to count existing submissions for the same index value. This is useful when the form needs to track how many times a person or record has already been submitted, as in the examples below:
Daily monitoring: If a form is used to survey the same person over time, you can link it to itself to count previous submissions. This can allow you to display a message (e.g., “monitoring is complete”) after a certain number of entries or to inform the enumerator of the number of forms submitted, as shown in the example below.
survey worksheet
type |
name |
label |
calculation |
|---|---|---|---|
xml-external |
monitoring |
||
text |
participant_id |
What is the participant’s ID? |
|
calculate |
count |
count(instance(‘monitoring’)/root/data[participant_id = current()/../participant_id]) |
|
note |
monitoring_note |
This participant has been surveyed ${count} times. |
|
survey |
Registration form: By linking a registration form to itself, you can check whether a user has already been registered. This can allow you to generate an error message or add a constraint if they are already registered, preventing duplicate registrations, as shown in the example below.
survey worksheet
type |
name |
label |
calculation |
relevant |
|---|---|---|---|---|
xml-external |
registration |
|||
text |
customer_id |
What is the customer’s ID number? |
||
calculate |
count |
count(instance(‘registration’)/root/data[customer_id = current()/../customer_id]) |
||
note |
already_registered |
This customer is already registered. Please close this form. |
${count} > 0 |
|
survey |
You can also use dynamic data attachments to retrieve values from the same project by linking the project to itself. However, this requires planning when you build the form.
When using this approach, the index variable must identify only one matching submission in the project data. If multiple submissions have the same index value (e.g., multiple submissions for a same unique ID), KoboToolbox may not know which row to retrieve data from. This can eventually result in an error, particularly in KoboCollect.
To avoid duplicate index values, you can set up the form to:
Use dynamic data attachments to count the number of existing submissions for the same index value.
Combine this count with the original index variable in a separate calculation. For example, submissions with the index value ID could be assigned unique values such as ID_0, ID_1, and ID_2.
survey worksheet
type |
name |
label |
calculation |
|---|---|---|---|
xml-external |
monitoring |
||
text |
participant_id |
What is the participant’s ID? |
|
calculate |
count |
count(instance(‘monitoring’)/root/data[participant_id = current()/../participant_id]) |
|
calculate |
id_count |
concat(${participant_id}, ‘_’, ${count}) |
|
survey |
Use this calculated value as the index variable when retrieving data from the project.
You can also use the position=1 argument to ensure that only one matching row is returned and reduce the risk of errors when multiple matches exist.
survey worksheet
type |
name |
label |
calculation |
|---|---|---|---|
xml-external |
monitoring |
||
text |
participant_id |
What is the participant’s ID? |
|
calculate |
count |
count(instance(‘monitoring’)/root/data[participant_id = current()/../participant_id]) |
|
calculate |
id_count |
concat(${participant_id}, ‘_’, ${count}) |
|
calculate |
enrollment_id |
concat(${participant_id}, ‘_0’) |
|
text |
full_name |
Participant full name |
instance(‘monitoring’)/root/data[id_count=current()/../enrollment_id][position()=1]/full_name |
survey |
In the example above:
participant_id is the unique ID of the participant
count is the number of existing submissions from this participant in the project data
id_count is a combination of the unique ID and the count of existing submissions
enrollment_id combines the unique ID and _0 to identify the first submission for this ID. It acts as the index variable for the following dynamic data attachment.
full_name pulls in the participant’s full name, as entered in the first submission, where id_count was equal to [id]_0
Data for dynamically linked projects can be collected using the KoboCollect Android app or web forms.
When collecting data, note the following:
The parent project must have at least one submission for the child project to function correctly.
When collecting data online, there is a five-minute delay in syncing new parent project data with the child project.
In offline mode, frequently download the child project to ensure data synchronization with the parent project.
Note: You can configure the KoboCollect Android app to automatically update the parent project's data when an internet connection is available. Go to Settings > Form management > Blank form update mode and select either Previously downloaded forms only or Exactly match server. You can set the automatic download frequency to occur every 15 minutes, every hour, every six hours, or every 24 hours. Note that enabling this setting may increase battery consumption.
name column of the survey worksheet.calculate questions outside the repeat group in the parent form and use them to store the repeat group values needed in the child form. Then, pull these calculated values into the child form instead of pulling directly from the repeat group.[position()=1] argument, as below:
instance('parent')/root/data[parent_index_group/parent_index_question = current()/../child_index_question][position()=1]/parent_group/parent_question
trigger column to your XLSForm. In the row that contains the dynamic data expression, enter the name of the child index question in the trigger column.
select_one or select_multiple question, dynamic data attachments will use the choice name, not the choice label.
To display the choice label in your child form instead, use one of the following approaches:
choices tab.jr:choice-name() calculation to retrieve the choice label, then pull that calculated value into the child form.Did you find what you were looking for? Was the information clear? Was anything missing?
Share your feedback to help us improve this article!
KoboToolbox is maintained by Kobo Inc.