Skip to content

Build A Production Gantt Project

This tutorial starts with any framework quick start and builds toward the same data flow used by the full showcase.

Source code
TypeScript ts
// src/components/gantt/GanttShowcase.ts
import './gantt-showcase.scss';
import { defineCustomElements } from '@revolist/revogrid/loader';
defineCustomElements();

import { GanttPlugin } from '@revolist/revogrid-enterprise';
import { ExportExcelPlugin, RowStatusPlugin } from '@revolist/revogrid-pro';
import {
  STANDARD_CALENDAR,
  SHOWCASE_ASSIGNMENTS,
  SHOWCASE_BASELINES,
  SHOWCASE_COLUMNS_WITH_COMPLETION,
  SHOWCASE_DEFAULT_HIDDEN,
  SHOWCASE_DEPENDENCIES,
  SHOWCASE_GANTT_CONFIG,
  SHOWCASE_RESOURCES,
  SHOWCASE_TASKS,
  renderShowcaseTaskBarColor,
  renderShowcaseTaskBarContent,
} from './gantt-project-data';
import { currentTheme } from '../composables/useRandomData';

// ─── Entry point ──────────────────────────────────────────────────────────────

export function load(parentSelector: string): void {
  const parent = document.querySelector(parentSelector);
  if (!parent) return;
  const darkTheme = currentTheme().isDark();

  const container = document.createElement('div');
  container.className = `gantt-showcase-shell grow h-full ${darkTheme ? 'gantt-showcase-shell--dark' : 'gantt-showcase-shell--light'}`;
  parent.appendChild(container);

  const grid = document.createElement('revo-grid') as HTMLRevoGridElement;
  const controls = document.createElement('div');
  controls.className = 'gantt-showcase-controls';
  container.appendChild(controls);

  grid.theme          = darkTheme ? 'darkCompact' : 'compact';
  grid.readonly       = false;
  grid.range          = true;
  grid.resize         = true;
  grid.rowSize        = 42;
  grid.rowHeaders     = false;
  grid.hideAttribution = true;
  grid.autoSizeColumn = false;
  grid.classList.add('gantt-showcase-grid');
  grid.plugins        = [GanttPlugin, ExportExcelPlugin, RowStatusPlugin];
  grid.hideColumns    = [...SHOWCASE_DEFAULT_HIDDEN];
  grid.columns        = [...SHOWCASE_COLUMNS_WITH_COMPLETION];
  grid.source         = [...SHOWCASE_TASKS];
  grid.ganttDependencies = [...SHOWCASE_DEPENDENCIES];
  grid.ganttCalendars    = [{ ...STANDARD_CALENDAR }];
  grid.ganttResources    = [...SHOWCASE_RESOURCES];
  grid.ganttAssignments  = [...SHOWCASE_ASSIGNMENTS];
  grid.ganttBaselines    = [...SHOWCASE_BASELINES];
  let showCriticalPath = Boolean(SHOWCASE_GANTT_CONFIG.visuals.showCriticalPath);
  let showBaseline = false;

  function applyGanttConfig() {
    grid.gantt = {
      ...SHOWCASE_GANTT_CONFIG,
      visuals: {
        ...SHOWCASE_GANTT_CONFIG.visuals,
        showCriticalPath,
        showBaseline,
        taskBarColorHook: renderShowcaseTaskBarColor,
        taskBarContentHook: renderShowcaseTaskBarContent,
      },
    } as typeof grid.gantt;
  }

  function createToggle(label: string, checked: () => boolean, onChange: (value: boolean) => void) {
    const control = document.createElement('label');
    control.className = 'gantt-showcase-control';
    const input = document.createElement('input');
    input.type = 'checkbox';
    input.className = 'gantt-showcase-control__input';
    const text = document.createElement('span');
    text.className = 'gantt-showcase-control__label';
    text.textContent = label;
    const sync = () => {
      input.checked = checked();
    };
    input.addEventListener('change', () => {
      onChange(input.checked);
      applyGanttConfig();
    });
    control.append(input, text);
    sync();
    return control;
  }

  controls.append(
    createToggle('Critical path', () => showCriticalPath, (value) => {
      showCriticalPath = value;
    }),
    createToggle('Baselines', () => showBaseline, (value) => {
      showBaseline = value;
    }),
  );

  applyGanttConfig();
  container.appendChild(grid);
}
React tsx
// src/components/gantt/GanttShowcase.tsx
import './gantt-showcase.scss';
import React, { useMemo, useRef, useState } from 'react';
import { RevoGrid } from '@revolist/react-datagrid';
import { GanttPlugin } from '@revolist/revogrid-enterprise';
import { ExportExcelPlugin, RowStatusPlugin } from '@revolist/revogrid-pro';
import {
  STANDARD_CALENDAR,
  SHOWCASE_ASSIGNMENTS,
  SHOWCASE_BASELINES,
  SHOWCASE_COLUMNS_WITH_COMPLETION,
  SHOWCASE_DEFAULT_HIDDEN,
  SHOWCASE_DEPENDENCIES,
  SHOWCASE_GANTT_CONFIG,
  SHOWCASE_RESOURCES,
  SHOWCASE_TASKS,
  renderShowcaseTaskBarColor,
  renderShowcaseTaskBarContent,
} from './gantt-project-data';
import type { GanttPluginConfig } from '@revolist/revogrid-enterprise';
import { currentTheme } from '../composables/useRandomData';

const plugins = [GanttPlugin, ExportExcelPlugin, RowStatusPlugin];
const source      = [...SHOWCASE_TASKS];
const dependencies = [...SHOWCASE_DEPENDENCIES];
const calendars    = [{ ...STANDARD_CALENDAR }];
const resources    = [...SHOWCASE_RESOURCES];
const assignments  = [...SHOWCASE_ASSIGNMENTS];
const baselines    = [...SHOWCASE_BASELINES];
const columns      = [...SHOWCASE_COLUMNS_WITH_COMPLETION];
const hiddenColumns = [...SHOWCASE_DEFAULT_HIDDEN];

function GanttShowcase() {
  const { isDark } = currentTheme();
  const darkTheme = isDark();
  const gridRef = useRef<HTMLRevoGridElement>(null);
  const [showCriticalPath, setShowCriticalPath] = useState(Boolean(SHOWCASE_GANTT_CONFIG.visuals.showCriticalPath));
  const [showBaseline, setShowBaseline] = useState(false);
  const ganttConfig: GanttPluginConfig = useMemo(() => ({
    ...SHOWCASE_GANTT_CONFIG,
    visuals: {
      ...SHOWCASE_GANTT_CONFIG.visuals,
      showCriticalPath,
      showBaseline,
      taskBarColorHook: renderShowcaseTaskBarColor,
      taskBarContentHook: renderShowcaseTaskBarContent,
    },
  } as GanttPluginConfig), [showCriticalPath, showBaseline]);

  return (
    <div className={`gantt-showcase-shell grow h-full ${darkTheme ? 'gantt-showcase-shell--dark' : 'gantt-showcase-shell--light'}`}>
      <div className="gantt-showcase-controls">
        <label className="gantt-showcase-control">
          <input
            className="gantt-showcase-control__input"
            type="checkbox"
            checked={showCriticalPath}
            onChange={(event) => setShowCriticalPath(event.currentTarget.checked)}
          />
          <span className="gantt-showcase-control__label">Critical path</span>
        </label>
        <label className="gantt-showcase-control">
          <input
            className="gantt-showcase-control__input"
            type="checkbox"
            checked={showBaseline}
            onChange={(event) => setShowBaseline(event.currentTarget.checked)}
          />
          <span className="gantt-showcase-control__label">Baselines</span>
        </label>
      </div>
      <RevoGrid
        ref={gridRef}
        className="gantt-showcase-grid"
        theme={darkTheme ? 'darkCompact' : 'compact'}
        hideAttribution
        readonly={false}
        range
        resize
        rowSize={42}
        rowHeaders={false}
        autoSizeColumn={false}
        plugins={plugins}
        hideColumns={hiddenColumns}
        source={source}
        columns={columns}
        gantt={ganttConfig}
        ganttDependencies={dependencies}
        ganttCalendars={calendars}
        ganttResources={resources}
        ganttAssignments={assignments}
        ganttBaselines={baselines}
      />
    </div>
  );
}

export default GanttShowcase;
Vue vue
<template>
  <div :class="shellClass">
    <div class="gantt-showcase-controls">
      <label class="gantt-showcase-control">
        <input v-model="showCriticalPath" class="gantt-showcase-control__input" type="checkbox" />
        <span class="gantt-showcase-control__label">Critical path</span>
      </label>
      <label class="gantt-showcase-control">
        <input v-model="showBaseline" class="gantt-showcase-control__input" type="checkbox" />
        <span class="gantt-showcase-control__label">Baselines</span>
      </label>
    </div>
    <RevoGrid
      ref="gridRef"
      class="gantt-showcase-grid skip-style cell-border"
      hide-attribution
      :readonly="false"
      :range="true"
      :resize="true"
      :row-size="42"
      :row-headers="false"
      :auto-size-column="false"
      :theme="gridTheme"
      :plugins="plugins"
      :hide-columns.prop="hiddenColumns"
      :source="source"
      :columns="columns"
      :gantt.prop="ganttConfig"
      :gantt-dependencies.prop="dependencies"
      :gantt-calendars.prop="calendars"
      :gantt-resources.prop="resources"
      :gantt-assignments.prop="assignments"
      :gantt-baselines.prop="baselines"
    />
  </div>
</template>

<script setup lang="ts">
import { computed, onMounted, ref } from 'vue';
import RevoGrid from '@revolist/vue3-datagrid';
import { ExportExcelPlugin, RowStatusPlugin } from '@revolist/revogrid-pro';
import {
  STANDARD_CALENDAR,
  SHOWCASE_ASSIGNMENTS,
  SHOWCASE_BASELINES,
  SHOWCASE_COLUMNS_WITH_COMPLETION,
  SHOWCASE_DEFAULT_HIDDEN,
  SHOWCASE_DEPENDENCIES,
  SHOWCASE_GANTT_CONFIG,
  SHOWCASE_RESOURCES,
  SHOWCASE_TASKS,
  renderShowcaseTaskBarColor,
  renderShowcaseTaskBarContent,
} from './gantt-project-data';
import { currentThemeVue } from '../composables/useRandomData';

// ── Static grid data ──────────────────────────────────────────────────────────
const plugins = ref<unknown[]>([]);
const source      = ref([...SHOWCASE_TASKS]);
const dependencies = ref([...SHOWCASE_DEPENDENCIES]);
const calendars    = ref([{ ...STANDARD_CALENDAR }]);
const resources    = ref([...SHOWCASE_RESOURCES]);
const assignments  = ref([...SHOWCASE_ASSIGNMENTS]);
const baselines    = ref([...SHOWCASE_BASELINES]);
const columns      = ref([...SHOWCASE_COLUMNS_WITH_COMPLETION]);
const hiddenColumns = [...SHOWCASE_DEFAULT_HIDDEN];
const showCriticalPath = ref(Boolean(SHOWCASE_GANTT_CONFIG.visuals.showCriticalPath));
const showBaseline = ref(false);
const { isDark } = currentThemeVue();
const gridTheme = computed(() => (isDark.value ? 'darkCompact' : 'compact'));
const shellClass = computed(() => [
  'gantt-showcase',
  'gantt-showcase-shell',
  'grow',
  'h-full',
  isDark.value ? 'gantt-showcase-shell--dark' : 'gantt-showcase-shell--light',
]);

const ganttConfig = computed(() => ({
  ...SHOWCASE_GANTT_CONFIG,
  visuals: {
    ...SHOWCASE_GANTT_CONFIG.visuals,
    showCriticalPath: showCriticalPath.value,
    showBaseline: showBaseline.value,
    taskBarColorHook: renderShowcaseTaskBarColor,
    taskBarContentHook: renderShowcaseTaskBarContent,
  },
}));

// ── Refs ──────────────────────────────────────────────────────────────────────
const gridRef    = ref<InstanceType<typeof RevoGrid> | HTMLRevoGridElement | null>(null);

onMounted(async () => {
  const { GanttPlugin } = await import('@revolist/revogrid-enterprise');

  plugins.value = [GanttPlugin, ExportExcelPlugin, RowStatusPlugin];
});
</script>

<style src="./gantt-showcase.scss" lang="scss"></style>

<style scoped>
.gantt-showcase :deep(revo-grid) {
  flex: 1;
  min-height: 0;
}
</style>
Angular ts
// src/components/gantt/GanttShowcaseAngular.ts
import {
  Component,
  NO_ERRORS_SCHEMA,
  ViewEncapsulation,
} from '@angular/core';
import { RevoGrid } from '@revolist/angular-datagrid';
import { GanttPlugin } from '@revolist/revogrid-enterprise';
import { ExportExcelPlugin, RowStatusPlugin } from '@revolist/revogrid-pro';
import type { GanttPluginConfig } from '@revolist/revogrid-enterprise';
import {
  STANDARD_CALENDAR,
  SHOWCASE_ASSIGNMENTS,
  SHOWCASE_BASELINES,
  SHOWCASE_COLUMNS_WITH_COMPLETION,
  SHOWCASE_DEFAULT_HIDDEN,
  SHOWCASE_DEPENDENCIES,
  SHOWCASE_GANTT_CONFIG,
  SHOWCASE_RESOURCES,
  SHOWCASE_TASKS,
  renderShowcaseTaskBarColor,
  renderShowcaseTaskBarContent,
} from './gantt-project-data';
import { currentTheme } from '../composables/useRandomData';

function createGanttConfig(showCriticalPath: boolean, showBaseline: boolean): GanttPluginConfig {
  return {
    ...SHOWCASE_GANTT_CONFIG,
    visuals: {
      ...SHOWCASE_GANTT_CONFIG.visuals,
      showCriticalPath,
      showBaseline,
      taskBarColorHook: renderShowcaseTaskBarColor,
      taskBarContentHook: renderShowcaseTaskBarContent,
    },
  } as GanttPluginConfig;
}

@Component({
  selector: 'gantt-showcase-grid',
  standalone: true,
  host: {
    class: 'gantt-showcase-angular-host',
  },
  // Allows Angular demos to bind RevoGrid plugin props that are not wrapper inputs.
  schemas: [NO_ERRORS_SCHEMA],
  imports: [RevoGrid],
  encapsulation: ViewEncapsulation.None,
  styleUrls: ['./gantt-showcase.scss'],
  template: `
    <div [class]="shellClass">
      <div class="gantt-showcase-controls">
        <label class="gantt-showcase-control">
          <input
            class="gantt-showcase-control__input"
            type="checkbox"
            [checked]="showCriticalPath"
            (change)="setCriticalPath($any($event.target).checked)"
          />
          <span class="gantt-showcase-control__label">Critical path</span>
        </label>
        <label class="gantt-showcase-control">
          <input
            class="gantt-showcase-control__input"
            type="checkbox"
            [checked]="showBaseline"
            (change)="setBaseline($any($event.target).checked)"
          />
          <span class="gantt-showcase-control__label">Baselines</span>
        </label>
      </div>
      <revo-grid
        class="gantt-showcase-grid skip-style cell-border"
        [theme]="theme"
        [hideAttribution]="true"
        [readonly]="false"
        [range]="true"
        [resize]="true"
        [rowSize]="42"
        [rowHeaders]="false"
        [autoSizeColumn]="false"
        [plugins]="plugins"
        [hideColumns]="hiddenColumns"
        [source]="source"
        [columns]="columns"
        [gantt]="ganttConfig"
        [ganttDependencies]="dependencies"
        [ganttCalendars]="calendars"
        [ganttResources]="resources"
        [ganttAssignments]="assignments"
        [ganttBaselines]="baselines"
      ></revo-grid>
    </div>
  `,
})
export class GanttShowcaseGridComponent {
  readonly isDark       = currentTheme().isDark();
  readonly theme        = this.isDark ? 'darkCompact' : 'compact';
  readonly shellClass   = `gantt-showcase-shell grow h-full ${this.isDark ? 'gantt-showcase-shell--dark' : 'gantt-showcase-shell--light'}`;
  readonly plugins      = [GanttPlugin, ExportExcelPlugin, RowStatusPlugin];
  showCriticalPath      = Boolean(SHOWCASE_GANTT_CONFIG.visuals.showCriticalPath);
  showBaseline          = false;
  ganttConfig           = createGanttConfig(this.showCriticalPath, this.showBaseline);
  readonly source       = [...SHOWCASE_TASKS];
  readonly dependencies = [...SHOWCASE_DEPENDENCIES];
  readonly calendars    = [{ ...STANDARD_CALENDAR }];
  readonly resources    = [...SHOWCASE_RESOURCES];
  readonly assignments  = [...SHOWCASE_ASSIGNMENTS];
  readonly baselines    = [...SHOWCASE_BASELINES];
  readonly columns      = [...SHOWCASE_COLUMNS_WITH_COMPLETION];
  readonly hiddenColumns = [...SHOWCASE_DEFAULT_HIDDEN];

  setCriticalPath(value: boolean): void {
    this.showCriticalPath = value;
    this.ganttConfig = createGanttConfig(this.showCriticalPath, this.showBaseline);
  }

  setBaseline(value: boolean): void {
    this.showBaseline = value;
    this.ganttConfig = createGanttConfig(this.showCriticalPath, this.showBaseline);
  }
}
  1. Add project hierarchy. Keep task rows flat and connect children with parentId.

    grid.source = [
    { id: 'delivery', name: 'Delivery', type: 'summary', startDate: '2026-07-14', duration: 8 },
    { id: 'design', parentId: 'delivery', name: 'Design', startDate: '2026-07-14', duration: 3 },
    { id: 'build', parentId: 'delivery', name: 'Build', startDate: '2026-07-17', duration: 5 },
    { id: 'launch', name: 'Launch', type: 'milestone', startDate: '2026-07-24', duration: 0 },
    ];
  2. Add dependencies. Use stable task IDs and one of FS, SS, FF, or SF.

    grid.ganttDependencies = [
    { id: 'd1', predecessorTaskId: 'design', successorTaskId: 'build', type: 'FS', lagDays: 0 },
    { id: 'd2', predecessorTaskId: 'build', successorTaskId: 'launch', type: 'FS', lagDays: 0 },
    ];
  3. Enable supported editing. The managed task columns route edits through Gantt scheduling and validation.

    grid.gantt = {
    ...grid.gantt,
    allowTaskCreate: true,
    taskCreateRow: true,
    contextMenu: {},
    };

    Inline cell editing, timeline drag/resize/progress handles, dependency drawing, the task editor, and the context menu all use the same project state. See Task Editing.

  4. Assign resources. Resources and assignments are separate collections so multiple resources can work on one task.

    grid.ganttResources = [{
    id: 'designer', name: 'Avery', role: 'Designer', calendarId: 'standard',
    allocationCapacity: 100, hourlyCost: 90,
    }];
    grid.ganttAssignments = [{
    id: 'a1', taskId: 'design', resourceId: 'designer',
    allocationUnits: 100, workHours: 24, responsibility: 'Visual design',
    }];
  5. Show schedule analysis. Critical path and baseline overlays are opt-in visual layers.

    grid.gantt = {
    ...grid.gantt,
    visuals: {
    ...grid.gantt?.visuals,
    showCriticalPath: true,
    showBaseline: true,
    baselineId: 'approved',
    },
    };
    grid.ganttBaselines = [{
    id: 'approved',
    name: 'Approved plan',
    capturedAt: '2026-07-14T00:00:00.000Z',
    tasks: [
    { taskId: 'design', startDate: '2026-07-14', endDate: '2026-07-16', duration: 24, progressPercent: 0 },
    { taskId: 'build', startDate: '2026-07-17', endDate: '2026-07-23', duration: 40, progressPercent: 0 },
    ],
    }];
  6. Persist complete project snapshots. Keep application state authoritative and replace grid collections after a successful save or server update.

    import { exportProjectSnapshot, parseProjectSnapshotJson } from '@revolist/revogrid-enterprise';
    const json = exportProjectSnapshot(snapshot, { space: 2 });
    const restored = parseProjectSnapshotJson(json);
    if (restored.ok) applyProject(restored.project);
  7. Add reporting actions. Grid CSV/Excel export covers task-table data; the print recipe prepares a combined task-table/timeline report.

    import { createPrintPdfExportRecipe } from '@revolist/revogrid-enterprise';
    const print = createPrintPdfExportRecipe({ target: document.querySelector('#gantt')!, document, window });
    print.exportProject(snapshot, { label: 'Delivery plan' });

Next, browse all Gantt examples, review configuration, or open the API reference.