Baseline in Vue Gantt Chart Component
19 Jun 202624 minutes to read
The baseline feature in the Gantt Chart component enables comparison between original planned schedules and actual task execution timelines. This visualization provides clear insights into schedule deviations, helping assess project performance and identify areas requiring attention. Baseline functionality displays both the original planned timeline and current progress side-by-side for comprehensive project tracking.
Before implementing baseline functionality, ensure the data source includes baseline date fields and configure the taskFields object with appropriate field mappings. The baseline feature requires proper field mapping to display planned versus actual timelines effectively.
Baseline fields:
- baselineStartDate: Represents the originally planned start date of a task. This value is used to compare against the actual start date to identify schedule deviations.
- baselineEndDate: Represents the originally planned end date of a task. It is used to compare against the actual end date.
-
baselineDuration: Represents the total planned duration of the task. This value is critical for baseline visualization. To represent a baseline milestone, this property must be explicitly set to
0. SettingbaselineStartDateandbaselineEndDateto the same value without settingbaselineDurationto0will result in a one-day baseline task, not a milestone.
Implement baseline
To enable baseline, configure the Gantt Chart component by setting renderBaseline to true, mapping baselineStartDate, baselineEndDate, and optionally baselineDuration in taskFields. To customize appearance set the baselineColor property or the .e-baseline-bar CSS class for advanced styling.
export let projectData = [
{
TaskID: 1,
TaskName: 'Project Planning',
StartDate: new Date('02/04/2019'),
EndDate: new Date('02/08/2019'),
baselineStartDate: new Date('02/02/2019'),
baselineEndDate: new Date('02/06/2019'),
baselineDuration: '5' // Regular baseline
},
{
TaskID: 2,
TaskName: 'Milestone Review',
StartDate: new Date('02/10/2019'),
EndDate: new Date('02/10/2019'),
baselineStartDate: new Date('02/09/2019'),
baselineEndDate: new Date('02/09/2019'),
baselineDuration: '0' // Milestone baseline
}
];
const baselineColor = 'rgba(255, 107, 107, 0.8)'; // Semi transparent baseline.e-gantt .e-gantt-chart .e-baseline-bar {
height: 4px;
border-radius: 2px;
opacity: 0.9;
background-color: #4CAF50;
}The following example demonstrates complete baseline configuration with proper field mapping:
<template>
<div>
<ejs-gantt ref='gantt' id="GanttContainer" :dataSource="data" :renderBaseline="true" baselineColor='red' :taskFields="taskFields" :height="height"></ejs-gantt>
</div>
</template>
<script setup>
import { provide } from "vue";
import { GanttComponent as EjsGantt, Selection } from "@syncfusion/ej2-vue-gantt";
import { baselineData } from './data-source.js';
const data = baselineData;
const taskFields = {
id: 'TaskID',
name: 'TaskName',
startDate: 'StartDate',
duration: 'Duration',
progress: 'Progress',
baselineStartDate:"BaselineStartDate",
baselineEndDate:"BaselineEndDate",
baselineDuration: "BaselineDuration",
parentID: 'ParentID'
}
const height = '430px';
provide('gantt', [Selection]);
</script><template>
<div>
<ejs-gantt ref='gantt' id="GanttContainer" :dataSource="data" :renderBaseline="true" baselineColor='red' :taskFields="taskFields" :height="height"></ejs-gantt>
</div>
</template>
<script>
import { GanttComponent, Selection } from "@syncfusion/ej2-vue-gantt";
import { baselineData } from './data-source.js';
export default {
name: "App",
components: {
"ejs-gantt":GanttComponent
},
data: function() {
return{
data: baselineData,
taskFields: {
id: 'TaskID',
name: 'TaskName',
startDate: 'StartDate',
duration: 'Duration',
progress: 'Progress',
baselineStartDate:"BaselineStartDate",
baselineEndDate:"BaselineEndDate",
baselineDuration: "BaselineDuration",
parentID: 'ParentID'
},
height: '430px',
};
},
provide: {
gantt: [ Selection ]
}
};
</script>Customize baseline templates
The baselineTemplate property allows customization of baseline rendering by replacing the default baseline UI with a custom HTML structure. This enables advanced scenarios such as rendering additional baseline elements, visual indicators, or multiple baselines using task-specific data.
Set the baselineTemplate property with a template string or function. The template receives the task data object, which can be used to dynamically generate baseline elements.
Multiple baseline rendering using template
By default, the Gantt component supports a single baseline per task. However, using the baselineTemplate, you can extend this behavior to render multiple baselines by maintaining additional baseline data within a custom field in your data source.
This enables rich visualization scenarios such as:
- Comparing original vs revised schedules.
- Visualizing multiple planning phases.
- Highlighting deviations across timeline checkpoints.
The following example demonstrates how to render multiple baselines using baselineTemplate.
<template>
<div>
<ejs-gantt ref='gantt' id="GanttContainer" :dataSource="data" :taskFields="taskFields"
:baselineTemplate="baselineTemplateFunc" :splitterSettings="splitterSettings" :tooltipSettings="tooltipSettings"
:allowSelection="true" :renderBaseline="true" :rowHeight="60" :taskbarHeight="20" gridLines="Both"
:highlightWeekends="true" :columns="columns" :labelSettings="labelSettings" :height="height"
baselineColor='red'></ejs-gantt>
</div>
</template>
<script setup>
import { ref, provide } from "vue";
import { GanttComponent as EjsGantt, Selection, DayMarkers } from "@syncfusion/ej2-vue-gantt";
import { baselineTemplateData } from './datasource.js';
const gantt = ref(null);
const data = baselineTemplateData;
const taskFields = {
id: 'TaskID',
name: 'TaskName',
startDate: 'StartDate',
endDate: 'EndDate',
duration: 'Duration',
progress: 'Progress',
baselineStartDate: 'BaselineStartDate',
baselineEndDate: 'BaselineEndDate',
parentID: 'ParentID'
};
const splitterSettings = {
columnIndex: 3,
};
const tooltipSettings = {
showTooltip: false,
};
const columns = [
{ field: 'TaskID', headerText: 'ID', textAlign: 'Left' },
{ field: 'TaskName', width: '270px', headerText: 'Name' },
{ field: 'BaselineStartDate', headerText: 'Baseline Start Date', width: '180px' },
{ field: 'BaselineDuration', headerText: 'Baseline Duration', width: '180px' },
{ field: 'BaselineStartDate1', format: { skeleton: 'yMd', type: 'date' }, headerText: 'Baseline1 Start Date', width: '180px' },
{ field: 'BaselineDuration1', headerText: 'Baseline1 Duration', width: '180px' },
{ field: 'BaselineStartDate2', format: { skeleton: 'yMd', type: 'date' }, headerText: 'Baseline2 Start Date', width: '180px' },
{ field: 'BaselineDuration2', headerText: 'Baseline2 Duration', width: '180px' }
];
const labelSettings = {
taskLabel: 'TaskName'
};
const height = '450px';
const baselineTemplateFunc = function () {
return {
template: function (props) {
if (props.hasChildRecords || (props.data && props.data.hasChildRecords)) {
return '';
}
const ganttInstance = gantt.value.ej2Instances;
const taskRecord = props.taskData;
const ganttProperties = taskRecord.ganttProperties;
const chartRowsModule = ganttInstance.chartRowsModule;
const baselineTop = chartRowsModule.baselineTop;
const baselineHeight = chartRowsModule.baselineHeight;
const taskBarHeight = chartRowsModule.taskBarHeight;
const milestoneHeight = chartRowsModule.milestoneHeight;
const milestoneMarginTop = chartRowsModule.milestoneMarginTop;
const rowHeight = ganttInstance.rowHeight;
const renderBaseline = ganttInstance.renderBaseline;
const enableRtl = ganttInstance.enableRtl;
const taskSpacing = 9;
const baselineSpacing = 4;
const getLeft = (date) => {
return ganttInstance.dataOperation.getTaskLeft(
new Date(date),
false,
ganttProperties.calendarContext
);
};
const getWidth = (start, duration) => {
if (!start || duration == null || duration === 0) return 0;
const end = new Date(start);
end.setDate(end.getDate() + duration);
const leftStart = ganttInstance.dataOperation.getTaskLeft(
new Date(start),
false,
ganttProperties.calendarContext
);
const leftEnd = ganttInstance.dataOperation.getTaskLeft(
end,
false,
ganttProperties.calendarContext
);
return leftEnd - leftStart;
};
const render = (start, duration, index) => {
if (!start) return '';
const leftPosition = getLeft(start);
const width = getWidth(start, duration);
if (duration === 0) {
const milestoneSize = renderBaseline ? taskBarHeight : (taskBarHeight - 10);
const baselineMilestoneHeight = renderBaseline ? 5 : 2;
const leftPosition_ms = enableRtl
? (leftPosition - (milestoneHeight / 2) + 3)
: (leftPosition - (milestoneHeight / 2) + 1);
const marginTop =
(-Math.floor(rowHeight - milestoneMarginTop) + baselineMilestoneHeight) +
2 +
(index * baselineSpacing);
return '<div class="e-baseline-gantt-milestone-container" style="position:absolute;' +
'width:' + milestoneSize + 'px;' +
'height:' + milestoneSize + 'px;' +
'transform:rotate(45deg);' +
(enableRtl ? 'right:' : 'left:') + leftPosition_ms + 'px;' +
'margin-top:' + marginTop + 'px;">' +
'</div>';
}
return '<div class="e-baseline-bar" role="term" style="position:absolute;' +
(enableRtl ? 'right:' : 'left:') + leftPosition + 'px;' +
'margin-top:' + (baselineTop + (index * taskSpacing)) + 'px;' +
'width:' + width + 'px;' +
'height:' + baselineHeight + 'px;"></div>';
};
return (
'<div class="custom-multi-baseline">' +
render(taskRecord.taskData.BaselineStartDate, taskRecord.taskData.BaselineDuration, 0) +
render(taskRecord.taskData.BaselineStartDate1, taskRecord.taskData.BaselineDuration1, 1) +
render(taskRecord.taskData.BaselineStartDate2, taskRecord.taskData.BaselineDuration2, 2) +
'</div>'
);
}
};
};
provide('gantt', [Selection, DayMarkers]);
</script><template>
<div>
<ejs-gantt ref='gantt' id="GanttContainer"
:dataSource="data"
:taskFields="taskFields"
:baselineTemplate="baselineTemplateFunc"
:splitterSettings="splitterSettings"
:tooltipSettings="tooltipSettings"
:allowSelection="true"
:renderBaseline="true"
:rowHeight="60"
:taskbarHeight="20"
gridLines="Both"
:highlightWeekends="true"
:columns="columns"
:labelSettings="labelSettings"
:height="height"
baselineColor='red'
></ejs-gantt>
</div>
</template>
<script>
import { GanttComponent, Selection, DayMarkers } from "@syncfusion/ej2-vue-gantt";
import { baselineTemplateData } from './data-source.js';
export default {
name: "App",
components: {
"ejs-gantt": GanttComponent
},
data: function() {
return {
data: baselineTemplateData,
taskFields: {
id: 'TaskID',
name: 'TaskName',
startDate: 'StartDate',
endDate: 'EndDate',
duration: 'Duration',
progress: 'Progress',
baselineStartDate: 'BaselineStartDate',
baselineEndDate: 'BaselineEndDate',
parentID: 'ParentID'
},
splitterSettings: {
columnIndex: 3,
},
tooltipSettings: {
showTooltip: false,
},
columns: [
{ field: 'TaskID', headerText: 'ID', textAlign: 'Left' },
{ field: 'TaskName', width: '270px', headerText: 'Name' },
{ field: 'BaselineStartDate', headerText: 'Baseline Start Date', width: '180px' },
{ field: 'BaselineDuration', headerText: 'Baseline Duration', width: '180px' },
{ field: 'BaselineStartDate1', format: { skeleton: 'yMd', type: 'date' }, headerText: 'Baseline1 Start Date', width: '180px' },
{ field: 'BaselineDuration1', headerText: 'Baseline1 Duration', width: '180px' },
{ field: 'BaselineStartDate2', format: { skeleton: 'yMd', type: 'date' }, headerText: 'Baseline2 Start Date', width: '180px' },
{ field: 'BaselineDuration2', headerText: 'Baseline2 Duration', width: '180px' }
],
labelSettings: {
taskLabel: 'TaskName'
},
height: '450px',
};
},
provide: {
gantt: [Selection, DayMarkers]
},
methods: {
baselineTemplateFunc: function() {
return {
template: {
template: function(props) {
if (props.hasChildRecords || (props.data && props.data.hasChildRecords)) {
return '';
}
const gantt = this.$refs.gantt.ej2Instances;
const taskRecord = props.taskData;
const ganttProperties = taskRecord.ganttProperties;
const chartRowsModule = gantt.chartRowsModule;
const baselineTop = chartRowsModule.baselineTop;
const baselineHeight = chartRowsModule.baselineHeight;
const taskBarHeight = chartRowsModule.taskBarHeight;
const milestoneHeight = chartRowsModule.milestoneHeight;
const milestoneMarginTop = chartRowsModule.milestoneMarginTop;
const rowHeight = gantt.rowHeight;
const renderBaseline = gantt.renderBaseline;
const enableRtl = gantt.enableRtl;
const taskSpacing = 9;
const baselineSpacing = 4;
const getLeft = (date) => {
return gantt.dataOperation.getTaskLeft(
new Date(date),
false,
ganttProperties.calendarContext
);
};
const getWidth = (start, duration) => {
if (!start || duration == null || duration === 0) return 0;
const end = new Date(start);
end.setDate(end.getDate() + duration);
const leftStart = gantt.dataOperation.getTaskLeft(
new Date(start),
false,
ganttProperties.calendarContext
);
const leftEnd = gantt.dataOperation.getTaskLeft(
end,
false,
ganttProperties.calendarContext
);
return leftEnd - leftStart;
};
const render = (start, duration, index) => {
if (!start) return '';
const leftPosition = getLeft(start);
const width = getWidth(start, duration);
if (duration === 0) {
const milestoneSize = renderBaseline ? taskBarHeight : (taskBarHeight - 10);
const baselineMilestoneHeight = renderBaseline ? 5 : 2;
const leftPosition_ms = enableRtl
? (leftPosition - (milestoneHeight / 2) + 3)
: (leftPosition - (milestoneHeight / 2) + 1);
const marginTop =
(-Math.floor(rowHeight - milestoneMarginTop) + baselineMilestoneHeight) +
2 +
(index * baselineSpacing);
return '<div class="e-baseline-gantt-milestone-container" style="position:absolute;' +
'width:' + milestoneSize + 'px;' +
'height:' + milestoneSize + 'px;' +
'transform:rotate(45deg);' +
(enableRtl ? 'right:' : 'left:') + leftPosition_ms + 'px;' +
'margin-top:' + marginTop + 'px;">' +
'</div>';
}
return '<div class="e-baseline-bar" role="term" style="position:absolute;' +
(enableRtl ? 'right:' : 'left:') + leftPosition + 'px;' +
'margin-top:' + (baselineTop + (index * taskSpacing)) + 'px;' +
'width:' + width + 'px;' +
'height:' + baselineHeight + 'px;"></div>';
};
return (
'<div class="custom-multi-baseline">' +
render(taskRecord.taskData.BaselineStartDate, taskRecord.taskData.BaselineDuration, 0) +
render(taskRecord.taskData.BaselineStartDate1, taskRecord.taskData.BaselineDuration1, 1) +
render(taskRecord.taskData.BaselineStartDate2, taskRecord.taskData.BaselineDuration2, 2) +
'</div>'
);
}
}
};
}
}
};
</script>