Task Dependency in React Gantt Chart Component

31 Jan 202624 minutes to read

Task dependency in the React Gantt Chart component establishes relationships between tasks, affecting scheduling where changes to predecessors impact successors. Dependencies are categorized into four types—Start to Start (SS), Start to Finish (SF), Finish to Start (FS), and Finish to Finish (FF)—mapped via the taskFields.dependency property in the data source. Parent dependencies are enabled by default with allowParentDependency set to true, allowing relationships between parent-parent, child-child, parent-child, and child-parent tasks. Offsets support day, hour, or minute units for precise timing, and validation modes handle conflicts during editing via the actionBegin event. Connector lines are customized using connectorLineWidth and connectorLineBackground, with the queryTaskbarInfo event enabling dynamic styling. Public methods like addPredecessor and removePredecessor allow programmatic management, ensuring accurate visualization with ARIA labels for accessibility and responsive scaling for mobile views.

Configure task dependencies

Task dependencies are defined in the data source as string values (e.g., ‘2FS+3d’ for Finish to Start with 3-day offset) and mapped using taskFields.dependency. Parent dependencies can be enabled by allowParentDependency property. By default, the allowParentDependency property will be true.

Multiple predecessor relationships can be defined in a single task by assigning a comma-separated string to the Predecessor field, such as '2FS,3FS'. This configuration allows the Gantt chart to interpret and render multiple dependencies during the initial data load.

{ TaskID: 4, TaskName: 'Soil test approval', StartDate: new Date('04/02/2024'), Duration: 0, Predecessor: '3FS,2FS', Progress: 30 }

The following example establishes dependencies:

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

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

    return (
        <GanttComponent
            height="430px"
            dataSource={data}
            taskFields={taskSettings}
        >
            <ColumnsDirective>
                <ColumnDirective field="TaskID" headerText="Task ID" width="100" />
                <ColumnDirective field="TaskName" headerText="Task Name" width="250" />
                <ColumnDirective field="StartDate" headerText="Start Date" width="150" />
                <ColumnDirective field="Duration" headerText="Duration" width="150" />
                <ColumnDirective field="Progress" headerText="Progress" width="150" />
            </ColumnsDirective>
            <Inject services={[]} />
        </GanttComponent>
    );
}

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

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

    return (
        <GanttComponent
            height="430px"
            dataSource={data}
            taskFields={taskSettings}
        >
            <ColumnsDirective>
                <ColumnDirective field="TaskID" headerText="Task ID" width="100" />
                <ColumnDirective field="TaskName" headerText="Task Name" width="250" />
                <ColumnDirective field="StartDate" headerText="Start Date" width="150" />
                <ColumnDirective field="Duration" headerText="Duration" width="150" />
                <ColumnDirective field="Progress" headerText="Progress" width="150" />
            </ColumnsDirective>
            <Inject services={[]} />
        </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/32.1.19/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>
<script src="https://cdn.syncfusion.com/ej2/syncfusion-helper.js" type ="text/javascript"></script>
</head>

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

</html>

This code renders connector lines for dependencies like ‘2FS’, updating taskbars on changes.

Understand task relationship types

Task relationships are categorized into four types based on start and finish dates:

  • Start to Start (SS): Successor starts with predecessor.

    Start to Start dependency

  • Start to Finish (SF): Successor finishes when predecessor starts.

    Start to Finish dependency

  • Finish to Start (FS): Successor starts after predecessor finishes (default).

    Finish to Start dependency

  • Finish to Finish (FF): Successor finishes with predecessor.

    Finish to Finish dependency

Specify types in the data source (e.g., ‘2SS+1h’) for hour-based offsets.

Configure predecessor offsets with duration units

Predecessor offsets support day, hour, or minute units (e.g., ‘2FS+3h’), allowing precise delays or leads between tasks.

The following example uses duration units:

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

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

    return (
        <GanttComponent
            height="430px"
            dataSource={data}
            taskFields={taskSettings}
        >
            <ColumnsDirective>
                <ColumnDirective field="TaskID" headerText="Task ID" textAlign="Left" width="100" />
                <ColumnDirective field="Predecessor" headerText="Dependency" width="150" />
                <ColumnDirective field="TaskName" headerText="Task Name" width="150" />
                <ColumnDirective field="StartDate" headerText="Start Date" width="150" />
                <ColumnDirective field="Duration" headerText="Duration" width="150" />
                <ColumnDirective field="Progress" headerText="Progress" width="150" />
            </ColumnsDirective>
            <Inject services={[]} />
        </GanttComponent>
    );
}

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

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

    return (
        <GanttComponent
            height="430px"
            dataSource={data}
            taskFields={taskSettings}
        >
            <ColumnsDirective>
                <ColumnDirective field="TaskID" headerText="Task ID" textAlign="Left" width="100" />
                <ColumnDirective field="Predecessor" headerText="Dependency" width="150" />
                <ColumnDirective field="TaskName" headerText="Task Name" width="150" />
                <ColumnDirective field="StartDate" headerText="Start Date" width="150" />
                <ColumnDirective field="Duration" headerText="Duration" width="150" />
                <ColumnDirective field="Progress" headerText="Progress" width="150" />
            </ColumnsDirective>
            <Inject services={[]} />
        </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/32.1.19/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>
<script src="https://cdn.syncfusion.com/ej2/syncfusion-helper.js" type ="text/javascript"></script>
</head>

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

</html>

This code sets offsets like ‘2FS+3h’, adjusting taskbars accordingly.

Disable automatic dependency offset updates

Automatic offset updates during taskbar editing are disabled with updateOffsetOnTaskbarEdit set to false, allowing manual updates via the dependency tab or predecessor column.

The following example disables automatic updates:

import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { GanttComponent, Inject, Edit, Toolbar } from '@syncfusion/ej2-react-gantt';
import { data } from './datasource';
function App() {
    const taskFields = {
        id: 'TaskID',
        name: 'TaskName',
        startDate: 'StartDate',
        duration: 'Duration',
        progress: 'Progress',
        dependency: 'Predecessor',
        parentID: 'ParentID'
    };
    const editSettings = {
        allowAdding: true,
        allowEditing: true,
        allowDeleting: true,
        allowTaskbarEditing: true,
        showDeleteConfirmDialog: true
    };
    const toolbar = ['Add', 'Edit', 'Update', 'Delete', 'Cancel'];
    return <GanttComponent dataSource={data} taskFields={taskFields} height='450px' updateOffsetOnTaskbarEdit={false} toolbar={toolbar} editSettings={editSettings}>
        <Inject services={[Edit, Toolbar]} />
    </GanttComponent>
};

ReactDOM.render(<App />, document.getElementById('root'));
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { GanttComponent, Inject, Edit, Toolbar, 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',
        dependency: 'Predecessor',
        parentID: 'ParentID'
    };
    const editSettings: EditSettingsModel = {
        allowAdding: true,
        allowEditing: true,
        allowDeleting: true,
        allowTaskbarEditing: true,
        showDeleteConfirmDialog: true
    };
    const toolbar: string[] = ['Add', 'Edit', 'Update', 'Delete', 'Cancel'];
    return <GanttComponent dataSource={data} taskFields={taskFields} height='450px' updateOffsetOnTaskbarEdit={true} toolbar={toolbar} editSettings={editSettings}>
        <Inject services={[Edit, Toolbar]} />
    </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/32.1.19/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>
<script src="https://cdn.syncfusion.com/ej2/syncfusion-helper.js" type ="text/javascript"></script>
</head>

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

</html>

This code preserves offsets during edits, requiring manual adjustments.

Handle dependency validation modes

Dependency validation during editing uses the actionBegin event with requestType: 'validateLinkedTask'. The validateMode argument defines modes:

  • respectLink: Prioritizes links, reverting invalid edits.
  • removeLink: Prioritizes editing, removing conflicting links.
  • preserveLinkWithEditing: Updates offsets to maintain links (default).

The following example enables respectLink mode:

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

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

    const editSettings = {
        allowTaskbarEditing: true
    };

    const actionBegin = (args) => {
        if (args.requestType === 'validateLinkedTask') {
            args.validateMode.respectLink = true;
        }
    };

    return (
        <GanttComponent
            id="ganttDefault"
            height="430px"
            dataSource={data}
            taskFields={taskSettings}
            editSettings={editSettings}
            actionBegin={actionBegin}
        >
            <ColumnsDirective>
                <ColumnDirective field="TaskID" headerText="Task ID" textAlign="Left" width="100" />
                <ColumnDirective field="TaskName" headerText="Task Name" width="150" />
                <ColumnDirective field="StartDate" headerText="Start Date" width="150" />
                <ColumnDirective field="Duration" headerText="Duration" width="150" />
                <ColumnDirective field="Predecessor" headerText="Dependency" width="150" />
                <ColumnDirective field="Progress" headerText="Progress" width="150" />
            </ColumnsDirective>
            <Inject services={[Edit]} />
        </GanttComponent>
    );
}

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

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

    const editSettings: EditSettingsModel = {
        allowTaskbarEditing: true
    };

    const actionBegin = (args: ActionEventArgs) => {
        if (args.requestType === 'validateLinkedTask') {
            (args as any).validateMode.respectLink = true;
        }
    };

    return (
        <GanttComponent
            id="ganttDefault"
            height="430px"
            dataSource={data}
            taskFields={taskSettings}
            editSettings={editSettings}
            actionBegin={actionBegin}
        >
            <ColumnsDirective>
                <ColumnDirective field="TaskID" headerText="Task ID" textAlign="Left" width="100" />
                <ColumnDirective field="TaskName" headerText="Task Name" width="150" />
                <ColumnDirective field="StartDate" headerText="Start Date" width="150" />
                <ColumnDirective field="Duration" headerText="Duration" width="150" />
                <ColumnDirective field="Predecessor" headerText="Dependency" width="150" />
                <ColumnDirective field="Progress" headerText="Progress" width="150" />
            </ColumnsDirective>
            <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/32.1.19/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>
<script src="https://cdn.syncfusion.com/ej2/syncfusion-helper.js" type ="text/javascript"></script>
</head>

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

</html>

This code reverts edits violating links, ensuring dependency integrity.

Use validation dialog

When all validation modes are disabled in actionBegin, a dialog prompts users to choose modes like canceling edits or removing links, based on the successor’s start date relative to the predecessor.

The following example enables the validation dialog:

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

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

    const editSettings = {
        allowTaskbarEditing: true
    };

    const actionBegin = (args) => {
        if (args.requestType === 'validateLinkedTask') {
            args.validateMode.preserveLinkWithEditing = false;
        }
    };

    return (
        <GanttComponent
            id="ganttDefault"
            height="430px"
            dataSource={data}
            taskFields={taskFields}
            editSettings={editSettings}
            actionBegin={actionBegin}
        >
            <ColumnsDirective>
                <ColumnDirective field="TaskID" headerText="Task ID" width="100" textAlign="Left" />
                <ColumnDirective field="Predecessor" headerText="Dependency" width="150" />
                <ColumnDirective field="TaskName" headerText="Task Name" width="150" />
                <ColumnDirective field="StartDate" headerText="Start Date" width="150" />
                <ColumnDirective field="Duration" headerText="Duration" width="150" />
                <ColumnDirective field="Progress" headerText="Progress" width="150" />
            </ColumnsDirective>
            <Inject services={[Edit]} />
        </GanttComponent>
    );
}

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

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

    const editSettings: EditSettingsModel = {
        allowTaskbarEditing: true
    };

    const actionBegin = (args: ActionBeginArgs): void => {
        if (args.requestType === 'validateLinkedTask') {
            (args as any).validateMode.preserveLinkWithEditing = false;
        }
    };

    return (
        <GanttComponent
            id="ganttDefault"
            height="430px"
            dataSource={data}
            taskFields={taskFields}
            editSettings={editSettings}
            actionBegin={actionBegin}
        >
            <ColumnsDirective>
                <ColumnDirective field="TaskID" headerText="Task ID" width="100" textAlign="Left" />
                <ColumnDirective field="Predecessor" headerText="Dependency" width="150" />
                <ColumnDirective field="TaskName" headerText="Task Name" width="150" />
                <ColumnDirective field="StartDate" headerText="Start Date" width="150" />
                <ColumnDirective field="Duration" headerText="Duration" width="150" />
                <ColumnDirective field="Progress" headerText="Progress" width="150" />
            </ColumnsDirective>
            <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/32.1.19/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>
<script src="https://cdn.syncfusion.com/ej2/syncfusion-helper.js" type ="text/javascript"></script>
</head>

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

</html>

This code displays options like “Remove the link and move the task” for conflicts.

Show or hide dependency lines dynamically

Dependency lines are hidden or shown by toggling visibility: hidden on .e-gantt-dependency-view-container, allowing dynamic control for focused views.

The following example toggles dependency lines:

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

function App() {
  let checked = false;

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

  const editSettings = {
    allowAdding: true,
    allowEditing: true,
    allowDeleting: true,
    allowTaskbarEditing: true,
    showDeleteConfirmDialog: true
  };

  const columns = [
    { field: 'TaskID', headerText: 'Task ID', width: '100', textAlign: 'Left' },
    { field: 'Predecessor', headerText: 'Dependency', width: '150' },
    { field: 'TaskName', headerText: 'Task Name', width: '150' },
    { field: 'StartDate', headerText: 'Start Date', width: '150' },
    { field: 'Duration', headerText: 'Duration', width: '150' },
    { field: 'Progress', headerText: 'Progress', width: '150' }
  ];

  const change = () => {
    const container = document.querySelector('.e-gantt-dependency-view-container');
    if (container) {
      container.style.visibility = checked ? 'visible' : 'hidden';
    }
    checked = !checked;
  };

  return (
    <div>
      <label>Show/Hide Dependency Line </label>
      <SwitchComponent onLabel="ON" offLabel="OFF" checked={checked} change={change} />
      <GanttComponent id="ganttDefault" height="430px" dataSource={data} taskFields={taskFields} editSettings={editSettings} columns={columns}>
        <Inject services={[Edit]} />
      </GanttComponent>
    </div>
  );
}

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 { SwitchComponent } from '@syncfusion/ej2-react-buttons';
import { data } from './datasource';

function App() {
  let checked: boolean = false;

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

  const editSettings: EditSettingsModel = {
    allowAdding: true,
    allowEditing: true,
    allowDeleting: true,
    allowTaskbarEditing: true,
    showDeleteConfirmDialog: true
  };

  const columns = [
    { field: 'TaskID', headerText: 'Task ID', width: '100', textAlign: 'Left' },
    { field: 'Predecessor', headerText: 'Dependency', width: '150' },
    { field: 'TaskName', headerText: 'Task Name', width: '150' },
    { field: 'StartDate', headerText: 'Start Date', width: '150' },
    { field: 'Duration', headerText: 'Duration', width: '150' },
    { field: 'Progress', headerText: 'Progress', width: '150' }
  ];

  const change = () => {
    const container = document.querySelector('.e-gantt-dependency-view-container') as HTMLElement;
    if (container) {
      container.style.visibility = checked ? 'visible' : 'hidden';
    }
    checked = !checked;
  };

  return (
    <div>
      <label>Show/Hide Dependency Line </label>
      <SwitchComponent onLabel="ON" offLabel="OFF" checked={checked} change={change} />
      <GanttComponent id="ganttDefault" height="430px" dataSource={data} taskFields={taskFields} editSettings={editSettings} columns={columns}>
        <Inject services={[Edit]} />
      </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/32.1.19/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>
    <script src="https://cdn.syncfusion.com/ej2/syncfusion-helper.js" type="text/javascript"></script>
  <script src="https://cdn.syncfusion.com/ej2/syncfusion-helper.js" type ="text/javascript"></script>
</head>

  <body>
    <div id="root">
      <div id="loader">Loading....</div>
    </div>
  </body>
</html>

This code hides lines on button click, with ARIA updates for accessibility.

Managing predecessor dependencies

You can manage task dependencies by adding, updating, or removing predecessor links. These methods control task order and execution:

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

function App() {
  let ganttObj = null;

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

  const editSettings = {
    allowTaskbarEditing: true,
    allowEditing: true,
    allowAdding: true,
    allowDeleting: true
  };

  const columns = [
    { field: 'TaskID', headerText: 'Task ID', width: '100', textAlign: 'Left' },
    { field: 'TaskName', headerText: 'Task Name', width: '150' },
    { field: 'StartDate', headerText: 'Start Date', width: '150' },
    { field: 'Duration', headerText: 'Duration', width: '150' },
    { field: 'Predecessor', headerText: 'Dependency', width: '150' },
    { field: 'Progress', headerText: 'Progress', width: '150' }
  ];

  const addDependency = () => {
    if (ganttObj) {
      ganttObj.addPredecessor(2, '3SF,7FS');
    }
  };

  const removeDependency = () => {
    if (ganttObj) {
      ganttObj.removePredecessor(4);
    }
  };

  const updateDependency = () => {
    if (ganttObj) {
      ganttObj.updatePredecessor(8, '7FS');
    }
  };

  return (<div>
    <div style=>
      <ButtonComponent onClick={addDependency}>Add Predecessor</ButtonComponent>
      <ButtonComponent onClick={removeDependency}>Remove Predecessor</ButtonComponent>
      <ButtonComponent onClick={updateDependency}>Update Predecessor</ButtonComponent>
    </div>
    <GanttComponent
      id="ganttDefault"
      height="430px"
      dataSource={data}
      taskFields={taskFields}
      editSettings={editSettings}
      columns={columns}
      ref={(gantt) => (ganttObj = gantt)}>
      <Inject services={[Edit]} />
    </GanttComponent></div>
  );
}

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 { ButtonComponent } from '@syncfusion/ej2-react-buttons';
import { data } from './datasource';

function App() {
  let ganttObj: GanttComponent | null = null;

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

  const editSettings: EditSettingsModel = {
    allowTaskbarEditing: true,
    allowEditing: true,
    allowAdding: true,
    allowDeleting: true
  };

  const columns = [
    { field: 'TaskID', headerText: 'Task ID', width: '100', textAlign: 'Left' },
    { field: 'TaskName', headerText: 'Task Name', width: '150' },
    { field: 'StartDate', headerText: 'Start Date', width: '150' },
    { field: 'Duration', headerText: 'Duration', width: '150' },
    { field: 'Predecessor', headerText: 'Dependency', width: '150' },
    { field: 'Progress', headerText: 'Progress', width: '150' }
  ];

  const addDependency = () => {
    if (ganttObj) {
      ganttObj.addPredecessor(2, '3SF,7FS');
    }
  };

  const removeDependency = () => {
    if (ganttObj) {
      ganttObj.removePredecessor(4);
    }
  };

  const updateDependency = () => {
    if (ganttObj) {
      ganttObj.updatePredecessor(8, '7FS');
    }
  };

  return (<div>
    <div style=>
      <ButtonComponent onClick={addDependency}>Add Predecessor</ButtonComponent>
      <ButtonComponent onClick={removeDependency}>Remove Predecessor</ButtonComponent>
      <ButtonComponent onClick={updateDependency}>Update Predecessor</ButtonComponent>
    </div>
    <GanttComponent
      id="ganttDefault"
      height="430px"
      dataSource={data}
      taskFields={taskFields}
      editSettings={editSettings}
      columns={columns}
      ref={(gantt) => (ganttObj = gantt)}>
      <Inject services={[Edit]} />
    </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/32.1.19/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>
    <script src="https://cdn.syncfusion.com/ej2/syncfusion-helper.js" type="text/javascript"></script>
  <script src="https://cdn.syncfusion.com/ej2/syncfusion-helper.js" type ="text/javascript"></script>
</head>

  <body>
    <div id="root">
      <div id="loader">Loading....</div>
    </div>
  </body>
</html>

Customize connector lines

Connector lines are styled globally with connectorLineWidth and connectorLineBackground.

The following example sets the connector line background color as red:

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

function App() {
  let ganttObj = null;

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

  const editSettings = {
    allowTaskbarEditing: true,
    allowEditing: true,
    allowAdding: true,
    allowDeleting: true
  };

  const columns = [
    { field: 'TaskID', headerText: 'Task ID', width: '100', textAlign: 'Left' },
    { field: 'TaskName', headerText: 'Task Name', width: '150' },
    { field: 'StartDate', headerText: 'Start Date', width: '150' },
    { field: 'Duration', headerText: 'Duration', width: '150' },
    { field: 'Predecessor', headerText: 'Dependency', width: '150' },
    { field: 'Progress', headerText: 'Progress', width: '150' }
  ];

  const addDependency = () => {
    if (ganttObj) {
      ganttObj.addPredecessor(2, '3SF,7FS');
    }
  };

  const removeDependency = () => {
    if (ganttObj) {
      ganttObj.removePredecessor(4);
    }
  };

  const updateDependency = () => {
    if (ganttObj) {
      ganttObj.updatePredecessor(8, '7FS');
    }
  };

  return (<div>
    <div style=>
      <ButtonComponent onClick={addDependency}>Add Predecessor</ButtonComponent>
      <ButtonComponent onClick={removeDependency}>Remove Predecessor</ButtonComponent>
      <ButtonComponent onClick={updateDependency}>Update Predecessor</ButtonComponent>
    </div>
    <GanttComponent
      id="ganttDefault"
      height="430px"
      dataSource={data}
      taskFields={taskFields}
      editSettings={editSettings}
      columns={columns}
      ref={(gantt) => (ganttObj = gantt)}>
      <Inject services={[Edit]} />
    </GanttComponent></div>
  );
}

ReactDOM.render(<App />, document.getElementById('root'));
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { GanttComponent, Inject, 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',
        dependency: 'Predecessor',
        parentID: 'ParentID'
    };

    return (
        <GanttComponent
            height="430px"
            dataSource={data}
            taskFields={taskFields}
            connectorLineWidth={2}
            connectorLineBackground="red"
        >
            <Inject services={[]} />
        </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/32.1.19/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>
    <script src="https://cdn.syncfusion.com/ej2/syncfusion-helper.js" type="text/javascript"></script>
  <script src="https://cdn.syncfusion.com/ej2/syncfusion-helper.js" type ="text/javascript"></script>
</head>

  <body>
    <div id="root">
      <div id="loader">Loading....</div>
    </div>
  </body>
</html>

Disable predecessor validation

By default, Gantt task dates are validated based on predecessor values. To disable this validation, set the enablePredecessorValidation property to false.

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

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

  return (
    <GanttComponent
      height="430px"
      dataSource={data}
      taskFields={taskFields}
      enablePredecessorValidation={false}
    >
      <Inject services={[]} />
    </GanttComponent>
  );
}

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

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

    return (
        <GanttComponent
            height="430px"
            dataSource={data}
            taskFields={taskFields}
            enablePredecessorValidation={false}
        >
            <Inject services={[]} />
        </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/32.1.19/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>
    <script src="https://cdn.syncfusion.com/ej2/syncfusion-helper.js" type="text/javascript"></script>
  <script src="https://cdn.syncfusion.com/ej2/syncfusion-helper.js" type ="text/javascript"></script>
</head>

  <body>
    <div id="root">
      <div id="loader">Loading....</div>
    </div>
  </body>
</html>

Limitation

When virtualization is enabled, dependency lines are shown only for tasks currently visible in the viewport. If two tasks are connected by a line, the line will appear only if at least one of the tasks is visible. If both tasks are expanded and the line spans across pages, it will still be displayed as long as one task is in view.

See also