Skip to main content
Back to examples
Professional demos/Big dataset
Loading live demo…from dhtmlxcode.com
Professional demos

Big dataset

Demonstrates DHTMLX Gantt performance with a large number of tasks and links, featuring optimized rendering.

gantt.plugins({ marker: true, tooltip: true });

gantt.config.row_height        = 22;
gantt.config.bar_height        = 17;
gantt.config.min_column_width  = 40;
gantt.config.scale_height      = 54;
gantt.config.work_time         = true;
gantt.config.date_format       = "%Y-%m-%d";

gantt.config.scales = [
	{ unit: "month", step: 1, format: "%M %Y" },
	{ unit: "week",  step: 1, format: "%d" }
];

gantt.templates.timeline_cell_class = function (task, date) {
	return gantt.isWorkTime({ date, task }) ? "" : "week_end";
};

/* ---------------------------------------------------------------------------
 *  HEADER CONTROLS
 * ------------------------------------------------------------------------ */
function pickTotal(n) {
	DHX.numberPicker.pick("inp_total", n, "#menu_total");
	clearLoadError();
}

function pickProj(n) {
	DHX.numberPicker.pick("inp_proj", n, "#menu_proj");
	clearLoadError();
}

DHX.dd.initClickOutside();

/* ---------------------------------------------------------------------------
 *  DATA GENERATOR
 * ------------------------------------------------------------------------ */
let TASK_NAMES = [
	"Design", "Development", "Testing", "Review", "Deploy",
	"Planning", "Analysis", "Implementation", "QA", "Research",
	"Prototype", "Integration", "Maintenance", "Documentation", "Sign-off"
];
let PROJECT_NAMES = [
	"Website Redesign", "Mobile App", "API Integration", "Cloud Migration",
	"Security Audit", "Data Pipeline", "Portal Launch", "Performance Tuning",
	"CRM Update", "DevOps Setup", "Analytics Dashboard", "Payment Gateway",
	"Onboarding Flow", "Notification System", "Search Refactor"
];

function makeRng(seed) {
	let state = seed >>> 0;
	return function () {
		state = (1664525 * state + 1013904223) >>> 0;
		return state / 4294967296;
	};
}

function rnd(rand, min, max) {
	return min + Math.floor(rand() * (max - min));
}

function fmtDate(d) {
	let dd = d.getDate(), mm = d.getMonth() + 1, yy = d.getFullYear();
	return `${yy}-${mm < 10 ? "0" : ""}${mm}-${dd < 10 ? "0" : ""}${dd}`;
}

let RANGE_START = new Date(2026, 0, 1);
let RANGE_DAYS  = 365;   /* 2026 */

function generateData(totalCount, projSize) {
	let tasks = [], links = [], id = 1;
	let rand = makeRng(totalCount * 31 + projSize * 17);
	let projectCount = Math.ceil(totalCount / projSize);

	for (let p = 0; p < projectCount && tasks.length < totalCount; p++) {
		let pStart = new Date(RANGE_START);
		let pOffset = rnd(rand, 0, RANGE_DAYS);
		pStart.setDate(RANGE_START.getDate() + (p === 0 ? 0 : pOffset));
		let pId   = id++;
		let pName = PROJECT_NAMES[p % PROJECT_NAMES.length];
		if (p >= PROJECT_NAMES.length) pName += ` ${Math.floor(p / PROJECT_NAMES.length) + 1}`;

		let childTasks = [];
		let cursor = new Date(pStart);
		let prevId = null;

		for (let t = 0; t < projSize && (tasks.length + childTasks.length) < totalCount; t++) {
			let tId  = id++;
			let tDur = rnd(rand, 3, 18);

			childTasks.push({
				id:         tId,
				text:       `${TASK_NAMES[rnd(rand, 0, TASK_NAMES.length)]} ${t + 1}`,
				start_date: fmtDate(cursor),
				duration:   tDur,
				progress:   Math.round(rand() * 10) / 10,
				parent:     pId
			});

			/* sequential 65 %, parallel overlap 35 % */
			let step = rand() > 0.35
				? rnd(rand, Math.ceil(tDur * 0.6), tDur + 2)
				: rnd(rand, 1, 4);
			cursor.setDate(cursor.getDate() + step);

			if (prevId && rand() > 0.2) {
				links.push({ id: links.length + 1, source: prevId, target: tId, type: "0" });
			}
			prevId = tId;
		}

		let pDur = Math.max(1, gantt.calculateDuration({ start_date: pStart, end_date: cursor }));

		tasks.push({
			id: pId, text: pName,
			type: "project", open: p < 5,
			start_date: fmtDate(pStart), duration: pDur, parent: 0
		});

		for (let i = 0; i < childTasks.length; i++) tasks.push(childTasks[i]);
	}
	return { tasks, links };
}

/* ---------------------------------------------------------------------------
 *  PERFORMANCE TOAST
 * ------------------------------------------------------------------------ */
function _toastRow(label, value) {
	return `<div class="dhx_perf_toast__row">
		<span class="dhx_perf_toast__label">${label}</span>
		<span class="dhx_perf_toast__val">${value}</span>
	</div>`;
}

function showPerfToast(tc, lc, genMs, parseMs, renderMs) {
	let el = document.getElementById("perf_toast");
	let totalMs = genMs + parseMs + renderMs;
	el.innerHTML = `<div class="dhx_perf_toast__title">Load statistics</div>
		${_toastRow("Tasks", tc.toLocaleString())}
		${_toastRow("Links", lc.toLocaleString())}
		<div class="dhx_perf_toast__divider"></div>
		${_toastRow("Generate", `${(genMs / 1000).toFixed(2)} s`)}
		${_toastRow("Parse", `${(parseMs / 1000).toFixed(2)} s`)}
		${_toastRow("Render", `${(renderMs / 1000).toFixed(2)} s`)}
		<div class="dhx_perf_toast__divider"></div>
		${_toastRow("Total", `${(totalMs / 1000).toFixed(2)} s`)}`;

	el.classList.add("visible");
}

/* ---------------------------------------------------------------------------
 *  LOAD
 * ------------------------------------------------------------------------ */
let startMarkerId = null;

function clearLoadError() {
	DHX.ui.setText("load_error", "");
	DHX.numberPicker.markError("field_total", false);
	DHX.numberPicker.markError("field_proj", false);
}

function setLoadWarning(message, fieldId) {
	DHX.ui.setText("load_error", message);
	DHX.numberPicker.markError("field_total", fieldId === "field_total");
	DHX.numberPicker.markError("field_proj",  fieldId === "field_proj");
}

function readBoundedNumber(inputId, fieldId, label, min, max = Infinity) {
	let value = parseInt(document.getElementById(inputId).value, 10);

	if (!Number.isFinite(value) || value < min) {
		setLoadWarning(
			`${label} must be at least ${min.toLocaleString()}.`,
			fieldId
		);
		return null;
	}

	if (value > max) {
		setLoadWarning(
			`${label} must not exceed ${max.toLocaleString()}.`,
			fieldId
		);
		return null;
	}
	return value;
}

function applyLoad() {
	clearLoadError();

	let total = readBoundedNumber("inp_total", "field_total", "Total tasks", 1);
	if (total == null) return;
	let proj = readBoundedNumber("inp_proj", "field_proj", "Tasks per project", 1, 500);
	if (proj == null) return;

	DHX.numberPicker.sync("#menu_total", total);
	DHX.numberPicker.sync("#menu_proj", proj);

	if (total > 200000) {
		setLoadWarning(
			"No Gantt task limit, but 200,000+ tasks may strain your browser or device.",
			"field_total"
		);

		requestAnimationFrame(function () {
			requestAnimationFrame(function () {
				load(total, proj);
			});
		});
	} else {
		load(total, proj);
	}
}

function load(totalCount, projSize) {
	document.getElementById("perf_toast").classList.remove("visible");
	gantt.clearAll();

	if (startMarkerId) {
		gantt.deleteMarker(startMarkerId);
		startMarkerId = null;
	}

	let t0    = performance.now();
	let data  = generateData(totalCount, projSize);
	let genMs = performance.now() - t0;

	let renderStart = performance.now();
	gantt.parse(data);
	let parseMs = performance.now() - t0 - genMs;

	startMarkerId = gantt.addMarker({ start_date: RANGE_START, css: "project_start", text: "Start" });

	let tc = gantt.getTaskCount(), lc = gantt.getLinkCount();
	gantt.showDate(RANGE_START);

	requestAnimationFrame(function () {
		requestAnimationFrame(function () {
			let renderMs = performance.now() - renderStart;
			showPerfToast(tc, lc, genMs, parseMs, renderMs);
		});
	});
}

/* ---------------------------------------------------------------------------
 *  INIT
 * ------------------------------------------------------------------------ */
function sizeGantt() {
	document.getElementById("gantt_here").style.height = (window.innerHeight - 60) + "px";
}
window.addEventListener("resize", sizeGantt);

sizeGantt();
gantt.init("gantt_here");
load(10000, 50);
<!DOCTYPE html>
<html lang="en">
<head>
	<meta http-equiv="Content-type" content="text/html; charset=utf-8">
	<title>Large Dataset Performance: dhtmlxGantt</title>
	<script src="../../codebase/dhtmlxgantt.js?v=10.0.0"></script>
	<link rel="stylesheet" href="../../codebase/dhtmlxgantt.css?v=10.0.0">
	<link rel="stylesheet" href="../common/demo-controls/dhx_controls.css">
	<script src="../common/demo-controls/dhx_controls.js"></script>
	<link rel="preconnect" href="https://fonts.googleapis.com">
	<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
	<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap">
	<style>
		#gantt_here { width: 100%; }

		.gantt_task_cell.week_end { background-color: var(--dhx-gantt-base-colors-background-alt); }

		.dhx_perf_toast {
			position: fixed;
			top: 72px;
			right: 18px;
			z-index: 200;
			background: var(--dhx-overlay);
			border: 1px solid var(--dhx-line);
			border-radius: 12px;
			box-shadow: 0 4px 16px rgba(16,24,40,.12), 0 1px 4px rgba(16,24,40,.06);
			padding: 14px 16px 12px;
			min-width: 220px;
			font: 13px var(--dhx-font);
			color: var(--dhx-ink-2);
			opacity: 0;
			transform: translateY(-6px);
			transition: opacity .22s ease, transform .22s ease;
			pointer-events: none;
		}
		.dhx_perf_toast.visible {
			opacity: 1;
			transform: translateY(0);
		}

		.dhx_perf_toast__title {
			font: 600 10px var(--dhx-font);
			color: var(--dhx-muted);
			text-transform: uppercase;
			letter-spacing: .6px;
			margin-bottom: 9px;
		}
		.dhx_perf_toast__row {
			display: flex;
			justify-content: space-between;
			align-items: baseline;
			gap: 16px;
			padding: 2px 0;
		}
		.dhx_perf_toast__label { color: var(--dhx-muted); }
		.dhx_perf_toast__val   { font-weight: 600; color: var(--dhx-ink); }
		.dhx_perf_toast__divider {
			height: 1px;
			background: var(--dhx-line);
			margin: 7px 0;
		}

		#load_error {
			display: inline-block;
			max-width: 400px;
			white-space: normal;
			overflow-wrap: anywhere;
			line-height: 1.35;
			text-align: left;
		}
	</style>
</head>
<body>

<div class="dhx_header dhx_header--centered">
	<div class="dhx_dd" id="dd_total">
		<div class="dhx_number" id="field_total">
			<span class="dhx_number__label">Total tasks</span>
			<input id="inp_total" class="dhx_number__input" type="number" min="1" value="10000" onkeydown="if(event.key==='Enter')applyLoad()">
			<button class="dhx_number__arrow" onclick="DHX.dd.toggle('dd_total')" tabindex="-1" type="button" aria-label="Choose total tasks">
				<svg class="dhx_ic" viewBox="0 0 12 12"><polyline points="2 4 6 8 10 4"/></svg>
			</button>
		</div>
		<div class="dhx_menu dhx_number_menu" id="menu_total">
			<div class="dhx_menu__item" data-value="1000" onclick="pickTotal(1000)">1 000</div>
			<div class="dhx_menu__item" data-value="5000" onclick="pickTotal(5000)">5 000</div>
			<div class="dhx_menu__item active checked" data-value="10000" onclick="pickTotal(10000)">10 000</div>
			<div class="dhx_menu__item" data-value="30000" onclick="pickTotal(30000)">30 000</div>
			<div class="dhx_menu__item" data-value="50000" onclick="pickTotal(50000)">50 000</div>
			<div class="dhx_menu__item" data-value="100000" onclick="pickTotal(100000)">100 000</div>
		</div>
	</div>

	<div class="dhx_dd" id="dd_proj">
		<div class="dhx_number" id="field_proj">
			<span class="dhx_number__label">Tasks per project</span>
			<input id="inp_proj" class="dhx_number__input" type="number" min="1" max="500" value="50" onkeydown="if(event.key==='Enter')applyLoad()">
			<button class="dhx_number__arrow" onclick="DHX.dd.toggle('dd_proj')" tabindex="-1" type="button" aria-label="Choose tasks per project">
				<svg class="dhx_ic" viewBox="0 0 12 12"><polyline points="2 4 6 8 10 4"/></svg>
			</button>
		</div>
		<div class="dhx_menu dhx_number_menu" id="menu_proj">
			<div class="dhx_menu__item" data-value="5" onclick="pickProj(5)">5</div>
			<div class="dhx_menu__item" data-value="10" onclick="pickProj(10)">10</div>
			<div class="dhx_menu__item" data-value="20" onclick="pickProj(20)">20</div>
			<div class="dhx_menu__item active checked" data-value="50" onclick="pickProj(50)">50</div>
			<div class="dhx_menu__item" data-value="100" onclick="pickProj(100)">100</div>
			<div class="dhx_menu__item" data-value="200" onclick="pickProj(200)">200</div>
		</div>
	</div>

	<button class="dhx_btn dhx_btn--primary" onclick="applyLoad()">Apply</button>
	<span class="dhx_form_error" id="load_error"></span>
</div>

<div id="gantt_here"></div>

<div class="dhx_perf_toast" id="perf_toast" role="status" aria-live="polite"></div>



</body>
</html>