Task Constraints in React Gantt Component
10 Jul 202524 minutes to read
Task constraints define rules that control when a task is allowed to start or finish in the project timeline. They help ensure that tasks follow a logical sequence, align with fixed deadlines, and make efficient use of resources. Constraints also support planning for real-world limitations like material delays, team availability, or mandatory compliance dates—making your schedule more realistic and reliable.
Benefits of using task constraints
Task constraints help guide the schedule of each task by applying real-world rules. They serve critical planning purposes and offer the following benefits:
- Enforce Task Logic: Ensure tasks follow a defined sequence, especially when one cannot begin until another ends.
- Align with Milestones: Anchor key tasks to fixed dates such as launches, reviews, or audits.
- Avoid Resource Conflicts: Prevent tasks from overlapping when they rely on the same resources or teams.
- Support Scenario Planning: Modify constraints to test “what-if” situations and explore how delays or accelerations affect the timeline.
- Meet Business and Compliance Deadlines: Guarantee that mandatory deadlines are met and ensure the schedule supports regulatory timelines.
- Improve Planning Accuracy: Account for real-world limitations like material availability or stakeholder input windows.
Understanding task constraint types
Constraint Type | Description | Example Use Case |
---|---|---|
As Soon As Possible (ASAP) | Starts the task immediately once its dependencies are cleared. | Begin development as soon as design is approved. |
As Late As Possible (ALAP) | Delays the task until the last possible moment without affecting successors. | Apply polish to UI just before release to use the latest assets. |
Must Start On (MSO) | Requires the task to begin on a fixed, non-negotiable date. | Partner company begins integration on July 1st per contract. |
Must Finish On (MFO) | Requires the task to end on a fixed date, regardless of its dependencies. | Submit compliance documentation by March 31 due to government regulations. |
Start No Earlier Than (SNET) | Prevents a task from starting before a certain date. | A campaign cannot begin until regulatory approval on August 15. |
Start No Later Than (SNLT) | Requires a task to start on or before a given date. | Financial reviews must begin by September 1 to meet reporting cycles. |
Finish No Earlier Than (FNET) | Ensures the task does not finish before a certain date. | Training can’t end before all participants have completed onboarding. |
Finish No Later Than (FNLT) | Ensures task completion on or before a specific date. | QA testing must be done by July 25 to meet release deadlines. |
Configuration and implementation
To enable and manage task constraints in the Gantt component, you need to configure specific fields under the taskFields
mapping. These fields tell the Gantt component which type of constraint to apply and the relevant date to enforce it.
Step 1: Define taskFields mappings
In your Gantt component configuration, map the following fields:
<GanttComponent
taskFields={{
id: 'taskId',
name: 'taskName',
startDate: 'startDate',
endDate: 'endDate',
constraintType: 'constraintType',
constraintDate: 'constraintDate'
}}
/>
These mappings ensure that each task can interpret and apply its constraints correctly based on your data source.
Step 2: Provide constraint data
In your project data source, ensure that each task includes values for the constraintType
and constraintDate
fields if constraints need to be applied.
Example data format:
{
"taskId": 1,
"taskName": "Design Approval",
"startDate": new Date("2025-07-01"),
"endDate": new Date("2025-07-02"),
"constraintType": 2,
"constraintDate": new Date("2025-07-01")
}
This task is constrained to must start on July 1, 2025.
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { GanttComponent, Inject, ColumnsDirective, ColumnDirective, Selection, Toolbar, DayMarkers, Edit } from '@syncfusion/ej2-react-gantt';
import { data } from './datasource';
function App() {
const getConstraintText = (value) => {
const map = {
0: "As Soon As Possible",
1: "As Late As Possible",
2: "Must Start On",
3: "Must Finish On",
4: "Start No Earlier Than",
5: "Start No Later Than",
6: "Finish No Earlier Than",
7: "Finish No Later Than",
};
const numValue = typeof value === "string" ? parseInt(value) : value;
return map[numValue] || "Unknown";
};
const taskFields = {
id: "TaskID",
name: "TaskName",
startDate: "StartDate",
endDate: "EndDate",
duration: "Duration",
progress: "Progress",
constraintType: "ConstraintType",
constraintDate: "ConstraintDate",
dependency: "Predecessor",
parentID: "parentID",
notes: "info",
};
const editSettings = {
allowAdding: true,
allowEditing: true,
allowDeleting: true,
allowTaskbarEditing: true,
showDeleteConfirmDialog: true,
};
const toolbar = [
"Add",
"Edit",
"Update",
"Delete",
"Cancel",
"ExpandAll",
"CollapseAll",
"Indent",
"Outdent",
];
const RightLabelTemplate = (props) => {
getConstraintText(props.ganttProperties.constraintType);
};
const templateRight = RightLabelTemplate;
const labelSettings = {
leftLabel: "TaskName",
rightLabel: templateRight.bind(this)
};
const splitterSettings = {
columnIndex: 4,
};
const projectStartDate = new Date("03/25/2025");
const projectEndDate = new Date("09/28/2025");
const eventMarkers = [
{ day: new Date("03/25/2025"), label: "Project StartDate" },
{ day: new Date("08/31/2025"), label: "Project EndDate" },
];
return (
<div className="control-section">
<GanttComponent
id="Constraint"
dataSource={data}
taskFields={taskFields}
editSettings={editSettings}
toolbar={toolbar}
allowSelection={true}
gridLines="Both"
highlightWeekends={true}
height="450px"
treeColumnIndex={1}
labelSettings={labelSettings}
splitterSettings={splitterSettings}
projectStartDate={projectStartDate}
projectEndDate={projectEndDate}
eventMarkers={eventMarkers}
>
<ColumnsDirective>
<ColumnDirective field="TaskID" visible={false} />
<ColumnDirective field="TaskName" headerText="Job Name" width="200" clipMode="EllipsisWithTooltip" />
<ColumnDirective field="StartDate" />
<ColumnDirective field="Duration" />
<ColumnDirective field="ConstraintType" width="180" />
<ColumnDirective field="ConstraintDate" />
<ColumnDirective field="EndDate" />
<ColumnDirective field="Predecessor" />
<ColumnDirective field="Progress" />
</ColumnsDirective>
<Inject services={[Edit, Selection, Toolbar, DayMarkers]} />
</GanttComponent>
</div>
)
};
ReactDOM.render(<App />, document.getElementById('root'));
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { GanttComponent, Inject, ColumnsDirective, ColumnDirective, Selection, Toolbar, DayMarkers, Edit } from '@syncfusion/ej2-react-gantt';
import { data } from './datasource';
function App() {
const getConstraintText = (value) => {
const map = {
0: "As Soon As Possible",
1: "As Late As Possible",
2: "Must Start On",
3: "Must Finish On",
4: "Start No Earlier Than",
5: "Start No Later Than",
6: "Finish No Earlier Than",
7: "Finish No Later Than",
};
const numValue = typeof value === "string" ? parseInt(value) : value;
return map[numValue] || "Unknown";
};
const taskFields = {
id: "TaskID",
name: "TaskName",
startDate: "StartDate",
endDate: "EndDate",
duration: "Duration",
progress: "Progress",
constraintType: "ConstraintType",
constraintDate: "ConstraintDate",
dependency: "Predecessor",
parentID: "parentID",
notes: "info",
};
const editSettings = {
allowAdding: true,
allowEditing: true,
allowDeleting: true,
allowTaskbarEditing: true,
showDeleteConfirmDialog: true,
};
const toolbar = [
"Add",
"Edit",
"Update",
"Delete",
"Cancel",
"ExpandAll",
"CollapseAll",
"Indent",
"Outdent",
];
const RightLabelTemplate = (props) => {
getConstraintText(props.ganttProperties.constraintType);
};
const templateRight = RightLabelTemplate;
const labelSettings = {
leftLabel: "TaskName",
rightLabel: templateRight.bind(this)
};
const splitterSettings = {
columnIndex: 4,
};
const projectStartDate = new Date("03/25/2025");
const projectEndDate = new Date("09/28/2025");
const eventMarkers = [
{ day: new Date("03/25/2025"), label: "Project StartDate" },
{ day: new Date("08/31/2025"), label: "Project EndDate" },
];
return (
<div className="control-section">
<GanttComponent
id="Constraint"
dataSource={data}
taskFields={taskFields}
editSettings={editSettings}
toolbar={toolbar}
allowSelection={true}
gridLines="Both"
highlightWeekends={true}
height="450px"
treeColumnIndex={1}
labelSettings={labelSettings}
splitterSettings={splitterSettings}
projectStartDate={projectStartDate}
projectEndDate={projectEndDate}
eventMarkers={eventMarkers}
>
<ColumnsDirective>
<ColumnDirective field="TaskID" visible={false} />
<ColumnDirective field="TaskName" headerText="Job Name" width="200" clipMode="EllipsisWithTooltip" />
<ColumnDirective field="StartDate" />
<ColumnDirective field="Duration" />
<ColumnDirective field="ConstraintType" width="180" />
<ColumnDirective field="ConstraintDate" />
<ColumnDirective field="EndDate" />
<ColumnDirective field="Predecessor" />
<ColumnDirective field="Progress" />
</ColumnsDirective>
<Inject services={[Edit, Selection, Toolbar, DayMarkers]} />
</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/30.1.37/material.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>
Managing scheduling conflicts due to constraint violations
When scheduling changes conflict with applied constraints, the Gantt component shows a violation popup to alert users. This validation applies specifically to strict constraint types:
- MustStartOn
- MustFinishOn
- StartNoLaterThan
- FinishNoLaterThan
Programmatic conflict management
You can intercept constraint violations using the actionBegin
event. When the event’s requestType
is "validateTaskViolation"
, set specific flags in args.validateMode
to determine how conflicts are handled.
Supported flags
Flag | Description |
---|---|
respectMustStartOn |
If true, silently rejects violations of MustStartOn. |
respectMustFinishOn |
If true, silently cancels changes violating MustFinishOn. |
respectStartNoLaterThan |
If true, blocks updates violating StartNoLaterThan. |
respectFinishNoLaterThan |
If true, blocks changes violating FinishNoLaterThan. |
Defaults: All flags default to false
, meaning violations show a popup. Setting a flag to true
enables silent enforcement (i.e., the user’s update is canceled without interruption).
Example setup
actionBegin={(args) => {
if (args.requestType === 'validateTaskViolation') {
args.validateMode = {
respectMustStartOn: true,
respectMustFinishOn: true,
respectStartNoLaterThan: true,
respectFinishNoLaterThan: true
};
}
}}
In the following example, we have disabled the MustStartOn violation popup by setting respectMustStartOn
to true
.
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { GanttComponent, Inject, ColumnsDirective, ColumnDirective, Selection, Toolbar, DayMarkers, Edit } from '@syncfusion/ej2-react-gantt';
import { data } from './datasource';
function App() {
const getConstraintText = (value) => {
const map = {
0: "As Soon As Possible",
1: "As Late As Possible",
2: "Must Start On",
3: "Must Finish On",
4: "Start No Earlier Than",
5: "Start No Later Than",
6: "Finish No Earlier Than",
7: "Finish No Later Than",
};
const numValue = typeof value === "string" ? parseInt(value) : value;
return map[numValue] || "Unknown";
};
const taskFields = {
id: "TaskID",
name: "TaskName",
startDate: "StartDate",
endDate: "EndDate",
duration: "Duration",
progress: "Progress",
constraintType: "ConstraintType",
constraintDate: "ConstraintDate",
dependency: "Predecessor",
parentID: "parentID",
notes: "info",
};
const editSettings = {
allowAdding: true,
allowEditing: true,
allowDeleting: true,
allowTaskbarEditing: true,
showDeleteConfirmDialog: true,
};
const toolbar = [
"Add",
"Edit",
"Update",
"Delete",
"Cancel",
"ExpandAll",
"CollapseAll",
"Indent",
"Outdent",
];
const RightLabelTemplate = (props) => {
getConstraintText(props.ganttProperties.constraintType);
};
const templateRight = RightLabelTemplate;
const labelSettings = {
leftLabel: "TaskName",
rightLabel: templateRight.bind(this)
};
const splitterSettings = {
columnIndex: 4,
};
const projectStartDate = new Date("03/25/2025");
const projectEndDate = new Date("09/28/2025");
const eventMarkers = [
{ day: new Date("03/25/2025"), label: "Project StartDate" },
{ day: new Date("08/31/2025"), label: "Project EndDate" },
];
const actionBegin = (args) => {
if (args.requestType === 'validateTaskViolation') {
args.validateMode.respectMustStartOn = true
}
};
return (
<div className="control-section">
<GanttComponent
id="Constraint"
dataSource={data}
taskFields={taskFields}
editSettings={editSettings}
toolbar={toolbar}
allowSelection={true}
actionBegin={actionBegin.bind(this)}
gridLines="Both"
highlightWeekends={true}
height="450px"
treeColumnIndex={1}
labelSettings={labelSettings}
splitterSettings={splitterSettings}
projectStartDate={projectStartDate}
projectEndDate={projectEndDate}
eventMarkers={eventMarkers}
>
<ColumnsDirective>
<ColumnDirective field="TaskID" visible={false} />
<ColumnDirective field="TaskName" headerText="Job Name" width="200" clipMode="EllipsisWithTooltip" />
<ColumnDirective field="StartDate" />
<ColumnDirective field="Duration" />
<ColumnDirective field="ConstraintType" width="180" />
<ColumnDirective field="ConstraintDate" />
<ColumnDirective field="EndDate" />
<ColumnDirective field="Predecessor" />
<ColumnDirective field="Progress" />
</ColumnsDirective>
<Inject services={[Edit, Selection, Toolbar, DayMarkers]} />
</GanttComponent>
</div>
)
};
ReactDOM.render(<App />, document.getElementById('root'));
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { GanttComponent, Inject, ColumnsDirective, ColumnDirective, Selection, Toolbar, DayMarkers, Edit } from '@syncfusion/ej2-react-gantt';
import { data } from './datasource';
function App() {
const getConstraintText = (value) => {
const map = {
0: "As Soon As Possible",
1: "As Late As Possible",
2: "Must Start On",
3: "Must Finish On",
4: "Start No Earlier Than",
5: "Start No Later Than",
6: "Finish No Earlier Than",
7: "Finish No Later Than",
};
const numValue = typeof value === "string" ? parseInt(value) : value;
return map[numValue] || "Unknown";
};
const taskFields = {
id: "TaskID",
name: "TaskName",
startDate: "StartDate",
endDate: "EndDate",
duration: "Duration",
progress: "Progress",
constraintType: "ConstraintType",
constraintDate: "ConstraintDate",
dependency: "Predecessor",
parentID: "parentID",
notes: "info",
};
const editSettings = {
allowAdding: true,
allowEditing: true,
allowDeleting: true,
allowTaskbarEditing: true,
showDeleteConfirmDialog: true,
};
const toolbar = [
"Add",
"Edit",
"Update",
"Delete",
"Cancel",
"ExpandAll",
"CollapseAll",
"Indent",
"Outdent",
];
const RightLabelTemplate = (props) => {
getConstraintText(props.ganttProperties.constraintType);
};
const templateRight = RightLabelTemplate;
const labelSettings = {
leftLabel: "TaskName",
rightLabel: templateRight.bind(this)
};
const splitterSettings = {
columnIndex: 4,
};
const projectStartDate = new Date("03/25/2025");
const projectEndDate = new Date("09/28/2025");
const eventMarkers = [
{ day: new Date("03/25/2025"), label: "Project StartDate" },
{ day: new Date("08/31/2025"), label: "Project EndDate" },
];
const actionBegin = (args) => {
if (args.requestType === 'validateTaskViolation') {
args.validateMode.respectMustStartOn = true
}
};
return (
<div className="control-section">
<GanttComponent
id="Constraint"
dataSource={data}
taskFields={taskFields}
editSettings={editSettings}
toolbar={toolbar}
allowSelection={true}
actionBegin={actionBegin.bind(this)}
gridLines="Both"
highlightWeekends={true}
height="450px"
treeColumnIndex={1}
labelSettings={labelSettings}
splitterSettings={splitterSettings}
projectStartDate={projectStartDate}
projectEndDate={projectEndDate}
eventMarkers={eventMarkers}
>
<ColumnsDirective>
<ColumnDirective field="TaskID" visible={false} />
<ColumnDirective field="TaskName" headerText="Job Name" width="200" clipMode="EllipsisWithTooltip" />
<ColumnDirective field="StartDate" />
<ColumnDirective field="Duration" />
<ColumnDirective field="ConstraintType" width="180" />
<ColumnDirective field="ConstraintDate" />
<ColumnDirective field="EndDate" />
<ColumnDirective field="Predecessor" />
<ColumnDirective field="Progress" />
</ColumnsDirective>
<Inject services={[Edit, Selection, Toolbar, DayMarkers]} />
</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/30.1.37/material.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>