---
title: Loop and Merge: Nested Loops
product: Form Library
description: Learn how to build nested loops in a form using Dynamic Panels and Dynamic Matrices. This JavaScript form builder demo shows how to create nested repeating question sets and display one input field per page.
framework: React
source: https://surveyjs.io/form-library/examples/nested-loops/reactjs
index: https://surveyjs.io/form-library/examples/overview.md
---

# Loop and Merge: Nested Loops (React)

SurveyJS Form Library supports nested loops through its Loop and Merge functionality. In a nested loop setup, an outer loop iterates over a primary list of items, while an inner loop handles a related set of sub-items. For example, the form below asks an organization owner to provide information about departments (outer loop) and the employees within each department (inner loop).

To implement a nested loop, use a [Dynamic Panel](/form-library/documentation/api-reference/dynamic-panel-model) as the outer loop and nest either another Dynamic Panel or a [Dynamic Matrix](/form-library/documentation/api-reference/dynamic-matrix-table-question-model) inside it. For a Dynamic Panel, define the repeated questions using the [`templateElements`](/form-library/documentation/api-reference/dynamic-panel-model#templateElements) property. For a nested Dynamic Matrix, configure the repeated fields using the [`columns`](/form-library/documentation/api-reference/dynamic-matrix-table-question-model#columns) property.

Each loop iteration displays a title. To customize titles in a Dynamic Panel, use the [`templateTitle`](/form-library/documentation/api-reference/dynamic-panel-model#templateTitle) property. For a Dynamic Matrix, use [`singleInputTitleTemplate`](/form-library/documentation/api-reference/matrix-table-with-dropdown-list#singleInputTitleTemplate). Both properties support dynamic placeholders and text piping from other questions and loop levels.

Nested loops can be enabled only if your survey displays one input field per page. To enable this structure, set the [`questionsOnPageMode`](/form-library/documentation/api-reference/survey-data-model#questionsOnPageMode) property to `"inputPerPage"` at the survey level.

## Files

### `public/index.html`

```html
<div id="surveyElement" style="position: absolute; top: 0; left: 0; right: 0; bottom: 0; min-height: 100%; height:100%"></div>
```

### `src/SurveyComponent.jsx`

```js
import React from "react";
import { Model } from "survey-core";
import { Survey } from "survey-react-ui";
import "survey-core/survey-core.min.css";
import "./index.css";
import { json } from "./json";

function SurveyComponent() {
    const survey = new Model(json);
    survey.onComplete.add((sender, options) => {
        console.log(JSON.stringify(sender.data, null, 3));
    });
    return (<Survey model={survey} />);
}

export default SurveyComponent;
```

### `src/index.css`

```css
/* You can add your custom CSS here. */
```

### `src/index.js`

```js
import React from "react";
import { createRoot } from "react-dom/client";
import SurveyComponent from "./SurveyComponent";

const root = createRoot(document.getElementById("surveyElement"));
root.render(<SurveyComponent />);
```

### `src/json.js`

```js
export const json = {
  "questionsOnPageMode": "inputPerPage",
  "pages": [
    {
      "name": "page1",
      "elements": [
        {
          "type": "paneldynamic",
          "name": "departments",
          "title": "List all departments in your company and their employees",
          "templateElements": [
            {
              "type": "text",
              "name": "department",
              "title": "Department name",
              "defaultDisplayValue": "[not set]",
              "isRequired": true
            },
            {
              "type": "matrixdynamic",
              "name": "employees",
              "title": "Add employees to the department",
              "columns": [
                {
                  "name": "employee-name",
                  "title": "Name",
                  "cellType": "text",
                  "isRequired": true,
                  "defaultDisplayValue": "[not set]"
                },
                {
                  "name": "employment-date",
                  "title": "Employment date",
                  "cellType": "text",
                  "inputType": "date"
                }
              ],
              "singleInputTitleTemplate": "Employee: {row.employee-name}",
              "rowCount": 0,
              "addRowText": "Add Employee",
              "removeRowText": "Remove Employee"
            }
          ],
          "templateTitle": "Department: {panel.department}",
          "panelCount": 1,
          "addPanelText": "Add Department"
        }
      ]
    }
  ]
};
```

### `src/theme.js`

```js
export const themeJson = {};
```

### `package.json`

```json
{
  "dependencies": {
    "react": "latest",
    "react-dom": "latest",
    "survey-core": "latest",
    "survey-react-ui": "latest"
  },
  "devDependencies": {
    "react-scripts": "latest"
  }
}
```

## Other Frameworks

- [Angular](https://surveyjs.io/form-library/examples/nested-loops/angular.md)
- [Vue 3](https://surveyjs.io/form-library/examples/nested-loops/vue3js.md)
- [jQuery](https://surveyjs.io/form-library/examples/nested-loops/jquery.md)
- [Vanilla JS](https://surveyjs.io/form-library/examples/nested-loops/vanillajs.md)
