Syncfusion AI Assistant

How can I help you?

Scrolling in React Gantt Chart Component

24 Mar 202624 minutes to read

Scrolling in the React Gantt Chart component enables smooth navigation across extensive project datasets and long timelines. It ensures taskbars, grid rows, and timeline cells remain visible within the viewport. Scrollbars automatically appear when content exceeds the component’s defined height and width, supporting vertical scrolling for rows, horizontal scrolling for columns, and timeline scrolling for extended chart areas.

Virtual scrolling enhances performance by rendering only the visible portion of the dataset. Scrollbars are equipped with ARIA labels for accessibility, making them compatible with screen readers. They also adapt to responsive layouts, although horizontal scrolling may be required on narrow screens with wide timelines.

By default, both height and width are set to auto.

Configure scrollbar display

Scrollbars appear based on content size:

  • Vertical scrollbar: Triggers when task row height exceeds the component’s height.
  • Horizontal scrollbar: Triggers when column width exceeds the tree grid pane.
  • Timeline scrollbar: Triggers when the timeline exceeds the chart area.

For precise layout control, set fixed dimensions using pixel values for both width and height.

import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { GanttComponent, Inject, Edit } from '@syncfusion/ej2-react-gantt';
import { data } from './datasource';
function App() {
    const taskFields = {
        id: 'TaskID',
        name: 'TaskName',
        startDate: 'StartDate',
        duration: 'Duration',
        progress: 'Progress',
        parentID: 'ParentID'
    };
    const editOptions = {
        allowAdding: true,
        allowEditing: true,
        allowDeleting: true,
        allowTaskbarEditing: true,
        showDeleteConfirmDialog: true
    };
    return <GanttComponent dataSource={data} taskFields={taskFields} editSettings={editOptions} width='600px' height='350px'>
        <Inject services={[Edit]} />
    </GanttComponent>
};
ReactDOM.render(<App />, document.getElementById('root'));
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { GanttComponent, Inject, Edit, EditSettingsModel, TaskFieldsModel } from '@syncfusion/ej2-react-gantt';
import { data } from './datasource';
function App() {
    const taskFields: TaskFieldsModel = {
        id: 'TaskID',
        name: 'TaskName',
        startDate: 'StartDate',
        duration: 'Duration',
        progress: 'Progress',
        parentID: 'ParentID'
    };
    const editOptions: EditSettingsModel = {
        allowAdding: true,
        allowEditing: true,
        allowDeleting: true,
        allowTaskbarEditing: true,
        showDeleteConfirmDialog: true
    };
    return <GanttComponent dataSource={data} taskFields={taskFields} editSettings={editOptions} width='600px' height='350px'>
        <Inject services={[Edit]} />
    </GanttComponent>
};
ReactDOM.render(<App />, document.getElementById('root'));
<!DOCTYPE html>
<html lang="en">

<head>
    <title>Syncfusion React Gantt</title>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta name="description" content="Essential JS 2 for React Components" />
    <meta name="author" content="Syncfusion" />
    <link href="https://cdn.syncfusion.com/ej2/33.2.3/tailwind3.css" rel="stylesheet" type="text/css"/>
    <link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet" />
    <script src="https://cdnjs.cloudflare.com/ajax/libs/systemjs/0.19.38/system.js"></script>
    <script src="systemjs.config.js"></script>
     <style>
        #loader {
            color: #008cff;
            height: 40px;
            left: 45%;
            position: absolute;
            top: 45%;
            width: 30%;
        }
		 .e-gantt .e-gantt-chart .e-custom-holiday {
           background-color:lightgreen;
        }
    </style>
</head>

<body>
        <div id='root'>
            <div id='loader'>Loading....</div>
        </div>
</body>

</html>

Configure responsive scrolling

You can make the Gantt Chart component responsive by setting its width and height to 100%, allowing it to fully occupy the parent container. When height is set to 100%, the parent element must have a defined height to support proper layout rendering. The Gantt will automatically adjust when the container is resized.

import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { GanttComponent } from '@syncfusion/ej2-react-gantt';
import { data } from './datasource';
function App() {
    const taskFields = {
        id: 'TaskID',
        name: 'TaskName',
        startDate: 'StartDate',
        duration: 'Duration',
        progress: 'Progress',
        parentID: 'ParentID'
    };
    return <GanttComponent dataSource={data} taskFields={taskFields} width='100%' height='100%'>    </GanttComponent>
};
ReactDOM.render(<App />, document.getElementById('root'));
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { GanttComponent, TaskFieldsModel } from '@syncfusion/ej2-react-gantt';
import { data } from './datasource';
function App() {
    const taskFields: TaskFieldsModel = {
        id: 'TaskID',
        name: 'TaskName',
        startDate: 'StartDate',
        duration: 'Duration',
        progress: 'Progress',
        parentID: 'ParentID'
    };
    return <GanttComponent dataSource={data} taskFields={taskFields} width='100%' height='100%'>    </GanttComponent>
};
ReactDOM.render(<App />, document.getElementById('root'));
<!DOCTYPE html>
<html lang="en">

<head>
    <title>Syncfusion React Gantt</title>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta name="description" content="Essential JS 2 for React Components" />
    <meta name="author" content="Syncfusion" />
    <link href="https://cdn.syncfusion.com/ej2/33.2.3/tailwind3.css" rel="stylesheet" type="text/css"/>
    <link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet" />
    <script src="https://cdnjs.cloudflare.com/ajax/libs/systemjs/0.19.38/system.js"></script>
    <script src="systemjs.config.js"></script>
     <style>
        #loader {
            color: #008cff;
            height: 40px;
            left: 45%;
            position: absolute;
            top: 45%;
            width: 30%;
        }
        .e-ganttresize {
            resize: both;
            overflow: auto;
            border: 1px solid red;
            padding: 10px;
            height: 300px;
            min-height: 250px;
            min-width: 250px;
        }
        .e-text{
            font-family: Helvetica, sans-serif;
            font-size: 14px;
        }
    </style>
</head>

<body>
        <div id='root' class='e-ganttresize'>
            <div id='loader'>Loading....</div>
        </div>
    </div>
</body>

</html>

Scroll to task row and timeline

The React Gantt Chart component provides built-in support for automatically scrolling to specific tasks and timeline positions, which is especially useful when working with large datasets.

To scroll vertically to a specific task row, use the selectRow method to select the desired task and apply setScrollTop to bring the selected row into view.

To scroll horizontally to a specific timeline date, use the scrollToDate method. This helps focus the timeline on a particular point in time.

To scroll directly to a specific task within the timeline, use the scrollToTask method with the task ID. This ensures the task is visible within the timeline view.

import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { GanttComponent, Selection, Inject } from '@syncfusion/ej2-react-gantt';
import { TextBoxComponent } from '@syncfusion/ej2-react-inputs';
import { ButtonComponent } from '@syncfusion/ej2-react-buttons';
import { data } from './datasource';
let ganttObj = null;
let rowInputObj = null;
let dateInputObj = null;
let taskInputObj = null;

function App() {
  const taskFields = {
    id: 'TaskID',
    name: 'TaskName',
    startDate: 'StartDate',
    duration: 'Duration',
    progress: 'Progress',
    parentID: 'ParentID',
  };
  // Select row by index and scroll vertically.
  function selectRow() {
    const value = parseInt(rowInputObj.value, 10);
    if (!isNaN(value)) {
      ganttObj.selectionModule.selectRow(value);
      const position = ganttObj.rowHeight * value;
      ganttObj.setScrollTop(position);
    }
  }
  // Scroll horizontally to a specific date.
  function scrollToDate() {
    const value = dateInputObj.value;
    if (value) {
      ganttObj.scrollToDate(value);
    }
  }
  // Scroll horizontally to a specific task by ID.
  function scrollToTask() {
    const value = taskInputObj.value;
    if (value) {
      ganttObj.scrollToTask(value);
    }
  }

  return (
    <div>
      <div style=>
        <div>
          <label>Row Index:</label>
          <TextBoxComponent
            ref={(e) => (rowInputObj = e)}
            width="150px"
            placeholder="e.g., 1"
          />
          <ButtonComponent onClick={selectRow}>Select Row</ButtonComponent>
        </div>

        <div>
          <label>Scroll to Date:</label>
          <TextBoxComponent
            ref={(e) => (dateInputObj = e)}
            width="150px"
            placeholder="MM/DD/YYYY"
          />
          <ButtonComponent onClick={scrollToDate}>Scroll Date</ButtonComponent>
        </div>

        <div>
          <label>Scroll to Task ID:</label>
          <TextBoxComponent
            ref={(e) => (taskInputObj = e)}
            width="150px"
            placeholder="e.g., 3"
          />
          <ButtonComponent onClick={scrollToTask}>Scroll Task</ButtonComponent>
        </div>
      </div>

      <div style=>
        <GanttComponent
          ref={(e) => (ganttObj = e)}
          height="315px"
          dataSource={data}
          taskFields={taskFields}
        >
          <Inject services={[Selection]} />
        </GanttComponent>
      </div>
    </div>
  );
}

ReactDOM.render(<App />, document.getElementById('root'));
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import {
  GanttComponent,
  TaskFieldsModel,
  Selection,
  Inject,
} from '@syncfusion/ej2-react-gantt';
import { TextBoxComponent } from '@syncfusion/ej2-react-inputs';
import { ButtonComponent } from '@syncfusion/ej2-react-buttons';
import { data } from './datasource';

let ganttObj: GanttComponent | null = null;
let rowInputObj: TextBoxComponent | null = null;
let dateInputObj: TextBoxComponent | null = null;
let taskInputObj: TextBoxComponent | null = null;

function App() {
  const taskFields: TaskFieldsModel = {
    id: 'TaskID',
    name: 'TaskName',
    startDate: 'StartDate',
    duration: 'Duration',
    progress: 'Progress',
    parentID: 'ParentID'
  };

  function selectRow(): void {
    const value: number = parseInt(rowInputObj.value as string, 10);
    if (!isNaN(value)) {
      ganttObj.selectionModule.selectRow(value);
      const position: number = (ganttObj as any).rowHeight * value;
      (ganttObj as any).setScrollTop(position);
    }
  }

  function scrollToDate(): void {
    const value: string = dateInputObj.value as string;
    if (value) {
      ganttObj.scrollToDate(value);
    }
  }

  function scrollToTask(): void {
    const value: string = taskInputObj.value as string;
    if (value) {
      ganttObj.scrollToTask(value);
    }
  }

  return (
    <div>
      <div style=>
        <div>
          <label>Row Index:</label>
          <TextBoxComponent
            ref={(e: TextBoxComponent) => (rowInputObj = e)}
            width='150px'
            placeholder='e.g., 1'
          />
          <ButtonComponent onClick={selectRow}>
            Select Row
          </ButtonComponent>
        </div>

        <div>
          <label>Scroll to Date:</label>
          <TextBoxComponent
            ref={(e: TextBoxComponent) => (dateInputObj = e)}
            width='150px'
            placeholder='MM/DD/YYYY'
          />
          <ButtonComponent onClick={scrollToDate}>
            Scroll Date
          </ButtonComponent>
        </div>

        <div>
          <label>Scroll to Task ID:</label>
          <TextBoxComponent
            ref={(e: TextBoxComponent) => (taskInputObj = e)}
            width='150px'
            placeholder='e.g., 3'
          />
          <ButtonComponent onClick={scrollToTask}>
            Scroll Task
          </ButtonComponent>
        </div>
      </div>

      <div style=>
        <GanttComponent
          ref={(e: GanttComponent) => (ganttObj = e)}
          height='315px'
          dataSource={data}
          taskFields={taskFields}
        >
          <Inject services={[Selection]} />
        </GanttComponent>
      </div>
    </div>
  );
}

ReactDOM.render(<App />, document.getElementById('root'));
<!DOCTYPE html>
<html lang="en">

<head>
    <title>Syncfusion React Gantt</title>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta name="description" content="Essential JS 2 for React Components" />
    <meta name="author" content="Syncfusion" />
    <link href="https://cdn.syncfusion.com/ej2/33.2.3/tailwind3.css" rel="stylesheet" type="text/css"/>
    <link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet" />
    <script src="https://cdnjs.cloudflare.com/ajax/libs/systemjs/0.19.38/system.js"></script>
    <script src="systemjs.config.js"></script>
     <style>
        #loader {
            color: #008cff;
            height: 40px;
            left: 45%;
            position: absolute;
            top: 45%;
            width: 30%;
        }
    </style>
</head>

<body>
        <div id='root'>
            <div id='loader'>Loading....</div>
        </div>
</body>

</html>

Synchronize horizontal scroll between Gantt charts

To synchronize horizontal scrolling across multiple Gantt Chart components, handle the actionComplete event with the HorizontalScroll action. This captures the scroll position of the first Gantt chart and applies it to the second Gantt using the scrollLeft property. This approach ensures aligned timeline navigation, which is especially useful for comparing related project data side-by-side, such as parallel schedules.

import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { GanttComponent, Inject, Selection } from '@syncfusion/ej2-react-gantt';
import { data } from './datasource';

let primaryGantt = null;
let secondaryGantt = null;

function App() {
  const taskFields = {
    id: 'TaskID',
    name: 'TaskName',
    startDate: 'StartDate',
    endDate: 'EndDate',
    duration: 'Duration',
    progress: 'Progress',
    dependency: 'Predecessor',
    parentID: 'ParentID'
  };

  function syncScroll(args) {
    if (args.action === 'HorizontalScroll') {
      const chart = secondaryGantt.element.querySelector(
        '.e-chart-root-container > div'
      );
      if (chart) {
        chart.scrollLeft = args.scrollLeft;
      }
    }
  }

  return (
    <div className="control-section" style=>
      <GanttComponent
        ref={(g) => (primaryGantt = g)}
        id="primaryGanttContainer"
        height="50%"
        width="100%"
        dataSource={data}
        taskFields={taskFields}
        treeColumnIndex={1}
        allowSelection={true}
        dateFormat="MMM dd, y"
        highlightWeekends={true}
        actionComplete={syncScroll}
      >
        <Inject services={[Selection]} />
      </GanttComponent>

      <GanttComponent
        ref={(g) => (secondaryGantt = g)}
        id="secondaryGanttContainer"
        height="50%"
        width="100%"
        dataSource={data}
        taskFields={taskFields}
        treeColumnIndex={1}
        allowSelection={true}
        dateFormat="MMM dd, y"
        highlightWeekends={true}
      >
        <Inject services={[Selection]} />
      </GanttComponent>
    </div>
  );
}

ReactDOM.render(<App />, document.getElementById('root'));
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import {
  GanttComponent,
  TaskFieldsModel,
  Inject,
  Selection
} from '@syncfusion/ej2-react-gantt';
import { data } from './datasource';

let primaryGantt: GanttComponent | null = null;
let secondaryGantt: GanttComponent | null = null;

function App() {
  const taskFields: TaskFieldsModel = {
    id: 'TaskID',
    name: 'TaskName',
    startDate: 'StartDate',
    endDate: 'EndDate',
    duration: 'Duration',
    progress: 'Progress',
    dependency: 'Predecessor',
    parentID: 'ParentID'
  };

  function syncScroll(args: any): void {
    if (args.action === 'HorizontalScroll') {
      const chart = secondaryGantt.element.querySelector(
        '.e-chart-root-container > div'
      ) as HTMLElement;
      if (chart) {
        chart.scrollLeft = args.scrollLeft;
      }
    }
  }

  return (
    <div className="control-section" style=>
      <GanttComponent
        ref={(g: GanttComponent) => (primaryGantt = g)}
        id="primaryGanttContainer"
        height="50%"
        width="100%"
        dataSource={data}
        taskFields={taskFields}
        treeColumnIndex={1}
        allowSelection={true}
        dateFormat="MMM dd, y"
        highlightWeekends={true}
        actionComplete={syncScroll}
      >
        <Inject services={[Selection]} />
      </GanttComponent>

      <GanttComponent
        ref={(g: GanttComponent) => (secondaryGantt = g)}
        id="secondaryGanttContainer"
        height="50%"
        width="100%"
        dataSource={data}
        taskFields={taskFields}
        treeColumnIndex={1}
        allowSelection={true}
        dateFormat="MMM dd, y"
        highlightWeekends={true}
      >
        <Inject services={[Selection]} />
      </GanttComponent>
    </div>
  );
}

ReactDOM.render(<App />, document.getElementById('root'));
<!DOCTYPE html>
<html lang="en">

<head>
    <title>Syncfusion React Gantt</title>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta name="description" content="Essential JS 2 for React Components" />
    <meta name="author" content="Syncfusion" />
    <link href="https://cdn.syncfusion.com/ej2/33.2.3/tailwind3.css" rel="stylesheet" type="text/css" />
    <link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet" />
    <script src="https://cdnjs.cloudflare.com/ajax/libs/systemjs/0.19.38/system.js"></script>
    <script src="systemjs.config.js"></script>
    <style>
        #loader {
            color: #008cff;
            height: 40px;
            left: 45%;
            position: absolute;
            top: 45%;
            width: 30%;
        }
    </style>
</head>

<body>
    <div id='root'>
        <div id='loader'>Loading....</div>
    </div>
</body>

</html>