Gantt Charts
A Gantt chart visualizes tasks against a time axis ā each task is drawn as a horizontal bar spanning its start and end dates. Optional metadata such as completion percentage, assignee, or priority can be attached to each bar. Typical use cases include project scheduling, sprint planning, and resource allocation.
SciChart.js has no dedicated Gantt series. Gantt charts are assembled from FastRectangleRenderableSeriesš for the task bars, a CategoryAxisš for the task rows on the Y axis, and a DateTimeNumericAxisš for the timeline on the X axis.
The JavaScript Gantt Chart Example can be found in the SciChart.JS Examples Suite on GitHub, or in the live demo at scichart.com/demo.
Core Building Blocksā
A Gantt chart in SciChart.js is assembled from:
- FastRectangleRenderableSeriesš ā renders each task as a horizontal bar
- XyxyDataSeriesš ā stores
[x, y, x1, y1]per task: start date, row bottom, end date, row top - EColumnMode.StartEndš ā interprets
xandx1as explicit start/end positions on the X axis - EColumnYMode.TopBottomš ā interprets
yandy1as explicit bottom/top positions on the Y axis - CategoryAxisš ā Y axis mapping integer row indices to task name strings;
flippedCoordinates: trueputs row 0 at the top - DateTimeNumericAxisš ā X axis with automatic date/time tick formatting; dates are passed as Unix millisecond timestamps
Examplesā
Basic Gantt Chartā
The simplest Gantt chart uses a plain NumericAxis on both axes and a FastRectangleRenderableSeries with EColumnMode.StartEnd and EColumnYMode.TopBottom. Each task occupies one integer row on the Y axis with a bar height less than 1 to leave gaps between rows.
This simplified example uses NumericAxis on both axes with integer coordinates. For the full approach with real dates and task labels, see the Project Timeline Example below.
// Each task is a horizontal bar defined by [xStart, yBottom, xEnd, yTop]
// Y rows are integer indices; bar height is 0.6 to leave gaps between rows
const BAR_HEIGHT = 0.6;
const tasks = [
{ start: 0, end: 3 },
{ start: 2, end: 6 },
{ start: 5, end: 12 },
{ start: 10, end: 14 },
{ start: 13, end: 15 },
];
const xValues = tasks.map(t => t.start);
const x1Values = tasks.map(t => t.end);
// Center each bar on its integer index so axis ticks land in the middle of each bar
const yValues = tasks.map((_, i) => i - BAR_HEIGHT / 2);
const y1Values = tasks.map((_, i) => i + BAR_HEIGHT / 2);
const ganttSeries = new FastRectangleRenderableSeries(wasmContext, {
dataSeries: new XyxyDataSeries(wasmContext, { xValues, yValues, x1Values, y1Values }),
columnXMode: EColumnMode.StartEnd,
columnYMode: EColumnYMode.TopBottom,
fill: "steelblue",
strokeThickness: 0,
opacity: 0.85,
topCornerRadius: 4,
bottomCornerRadius: 4
});
Data Formatā
For a real project timeline, convert task objects into the four flat arrays that XyxyDataSeries expects. Dates are passed as Unix millisecond timestamps via Date.getTime(). Metadata attached to each data point is later used by tooltips and data labels.
The prepareGanttData function from the project-timeline demo:
function prepareGanttData(tasks: GanttTask[]) {
const xValues: number[] = [];
const yValues: number[] = [];
const x1Values: number[] = [];
const y1Values: number[] = [];
const metadata: { isSelected: boolean; name: string; start: Date; end: Date; percentComplete: number }[] = [];
tasks.forEach((task, i) => {
xValues.push(task.startDate.getTime());
x1Values.push(task.endDate.getTime());
// CategoryAxis is reversed: index 0 = top row, so y increases downward
yValues.push(i);
y1Values.push(i + BAR_HEIGHT);
metadata.push({ isSelected: false, name: task.name, start: task.startDate, end: task.endDate, percentComplete: task.percentComplete });
});
return { xValues, yValues, x1Values, y1Values, metadata };
}
Metadata is attached to the XyxyDataSeries constructor:
new XyxyDataSeries(wasmContext, { xValues, yValues, x1Values, y1Values, metadata })
Axis Setupā
DateTimeNumericAxis (X)ā
The X axis uses DateTimeNumericAxis with labelFormat: ENumericFormat.Date_DDMMYYYY so tick labels are automatically formatted as readable dates:
// X axis: date/time timeline
sciChartSurface.xAxes.add(new DateTimeNumericAxis(wasmContext, {
labelFormat: ENumericFormat.Date_DDMMYYYY,
growBy: new NumberRange(0.02, 0.05)
}));
CategoryAxis (Y)ā
The Y axis uses CategoryAxis with flippedCoordinates: true so that row index 0 appears at the top (matching typical Gantt chart conventions). A custom formatLabel on the label provider maps integer row indices to task name strings:
// Y axis: category labels for each project stage, reversed so row 0 is at the top
const yAxis = new CategoryAxis(wasmContext, {
axisAlignment: EAxisAlignment.Left,
flippedCoordinates: true,
growBy: new NumberRange(0.05, 0.05),
labelStyle: { padding: { left: 0, right: 0, top: 0, bottom: 40 } }
});
// Map integer row indices to task name strings
yAxis.labelProvider.formatLabel = (dataValue: number) =>
TASKS[Math.round(dataValue)]?.name ?? "";
sciChartSurface.yAxes.add(yAxis);
Here TASKS is the module-level array of GanttTask objects defined in the Data Format section.
Stylingā
Task bars support rounded corners, opacity, stroke, and data labels. The metaDataSelector callback reads the completion percentage from the per-point metadata and renders it inside each bar.
The snippet below assumes metadata has been prepared by the prepareGanttData function shown in the Data Format section above.
const ganttSeries = new FastRectangleRenderableSeries(wasmContext, {
dataSeries: new XyxyDataSeries(wasmContext, { xValues, yValues, x1Values, y1Values, metadata }),
columnXMode: EColumnMode.StartEnd,
columnYMode: EColumnYMode.TopBottom,
fill: "#4a90d9",
stroke: "white",
strokeThickness: 1,
opacity: 0.5,
topCornerRadius: 4,
bottomCornerRadius: 4,
dataLabels: {
style: { fontSize: 14, fontFamily: "Arial" },
color: "white",
// display completion percentage from metadata
metaDataSelector: (m) => {
const meta = m as { isSelected: boolean; name: string; start: Date; end: Date; percentComplete: number };
return meta ? `${meta.percentComplete}%` : "";
}
}
});
Tooltipsā
A CursorModifier with a custom tooltipDataTemplate reads the point metadata to show the task name, start date, end date, and completion percentage on hover:
// Tooltip: show task name, start date, end date on hover
const tooltipDataTemplate: TCursorTooltipDataTemplate = (seriesInfos) => {
return seriesInfos
.filter(si => si.isHit)
.map(si => {
const m = si.pointMetadata as { isSelected: boolean; name: string; start: Date; end: Date; percentComplete: number };
if (!m) return "";
return [
m.name,
`Start: ${m.start.toLocaleDateString()}`,
`End: ${m.end.toLocaleDateString()}`,
`Complete: ${m.percentComplete}%`
].join("\n");
});
};
Chart Modifiersā
The project timeline uses pan and zoom restricted to the X direction so the task list on the Y axis stays fixed, plus a CursorModifier for tooltips:
sciChartSurface.chartModifiers.add(
new ZoomPanModifier({ xyDirection: EXyDirection.XDirection }),
new ZoomExtentsModifier(),
new MouseWheelZoomModifier({ xyDirection: EXyDirection.XDirection }),
new CursorModifier({ showTooltip: true, tooltipDataTemplate })
);
Project Timeline Exampleā
The full project timeline example adds a CategoryAxis, DateTimeNumericAxis, per-task metadata, data labels showing completion percentage, and interactive tooltips via CursorModifier.
// X axis: date/time timeline
sciChartSurface.xAxes.add(new DateTimeNumericAxis(wasmContext, {
growBy: new NumberRange(0.02, 0.05)
}));
// Y axis: category labels for each project stage, reversed so row 0 is at the top
const yAxis = new CategoryAxis(wasmContext, {
axisAlignment: EAxisAlignment.Left,
flippedCoordinates: true,
visibleRange: new NumberRange(-0.5, TASKS.length - 0.5),
autoRange: EAutoRange.Never,
drawMajorBands: false,
drawMinorGridLines: false,
});
// Map integer row indices to task name strings
yAxis.labelProvider.formatLabel = (dataValue: number) =>
TASKS[Math.round(dataValue)]?.name ?? "";
sciChartSurface.yAxes.add(yAxis);
const { xValues, yValues, x1Values, y1Values, metadata } = prepareGanttData(TASKS);
const ganttSeries = new FastRectangleRenderableSeries(wasmContext, {
dataSeries: new XyxyDataSeries(wasmContext, { xValues, yValues, x1Values, y1Values, metadata }),
columnXMode: EColumnMode.StartEnd,
columnYMode: EColumnYMode.TopBottom,
fill: "#4a90d9",
stroke: "white",
strokeThickness: 1,
opacity: 0.5,
topCornerRadius: 4,
bottomCornerRadius: 4,
dataLabels: {
style: { fontSize: 14, fontFamily: "Arial" },
color: "white",
// display completion percentage from metadata
metaDataSelector: (m) => {
const meta = m as { isSelected: boolean; name: string; start: Date; end: Date; percentComplete: number };
return meta ? `${meta.percentComplete}%` : "";
}
}
});
sciChartSurface.renderableSeries.add(ganttSeries);
// Tooltip: show task name, start date, end date on hover
const tooltipDataTemplate: TCursorTooltipDataTemplate = (seriesInfos) => {
return seriesInfos
.filter(si => si.isHit)
.map(si => {
const m = si.pointMetadata as { isSelected: boolean; name: string; start: Date; end: Date; percentComplete: number };
if (!m) return "";
return [
m.name,
`Start: ${m.start.toLocaleDateString()}`,
`End: ${m.end.toLocaleDateString()}`,
`Complete: ${m.percentComplete}%`
].join("\n");
});
};
sciChartSurface.chartModifiers.add(
new ZoomPanModifier({ xyDirection: EXyDirection.XDirection }),
new ZoomExtentsModifier(),
new MouseWheelZoomModifier({ xyDirection: EXyDirection.XDirection }),
new CursorModifier({ showTooltip: true, tooltipDataTemplate })
);