Server-Side Rendering (SSR)
Live example
Section titled “Live example”This page is rendered by Astro and mounts the Vue grid through a client-only boundary. Expand the source preview to inspect the framework component; the wrapper code below shows the SSR boundary.
Source code
import { defineCustomElements } from '@revolist/revogrid/loader';defineCustomElements();
import { GanttPlugin, createDefaultTaskTableColumn } from '@revolist/gantt';import { currentTheme } from '../composables/useRandomData';import { calendars, dependencies, ganttConfig, initiallyHiddenColumnProps, tasks, taskTableColumnProps } from './gantt-basic-data';import './gantt-basic.css';
const { isDark } = currentTheme();const columns = taskTableColumnProps.map((prop) => createDefaultTaskTableColumn(prop));
export function load(parentSelector: string) { const parent = document.querySelector(parentSelector); if (!parent) { return () => {}; }
const root = document.createElement('section'); const grid = document.createElement('revo-grid');
root.className = 'gantt-basic-demo'; grid.className = 'gantt-basic-grid'; grid.theme = isDark() ? 'darkCompact' : 'compact'; grid.hideAttribution = true; grid.canMoveColumns = true; grid.plugins = [GanttPlugin]; grid.gantt = ganttConfig; grid.ganttCalendars = calendars; grid.ganttDependencies = dependencies; grid.columns = columns; grid.hideColumns = [...initiallyHiddenColumnProps];
root.appendChild(grid); parent.appendChild(root);
grid.source = tasks.map((task) => ({ ...task }));
return () => root.remove();}import type { DependencyEntity, GanttToolbarColumnOption, GanttTaskSourceRow, TaskTableColumnProp,} from '@revolist/gantt';type CalendarEntity = NonNullable<import('@revolist/gantt').GanttPluginConfig['calendars']>[number];
export const PROJECT_ID = 'project-web-redesign';export const CALENDAR_ID = 'cal-standard';
export const ganttConfig = { id: PROJECT_ID, name: 'Website Redesign', version: '1', currency: 'USD', timeZone: 'UTC', primaryCalendarId: CALENDAR_ID, updatedAt: '2026-04-06T00:00:00Z', statusDate: '2026-04-06', zoomPreset: 'day-week' as const, allowTaskCreate: true, taskCreateRow: true, scheduling: { excludeHolidaysFromDuration: true, },};
export const taskTableColumnProps: TaskTableColumnProp[] = [ 'wbs', 'name', 'status',];
export const initiallyHiddenColumnProps: TaskTableColumnProp[] = ['wbs'];
const initiallyVisibleColumnProps = new Set<TaskTableColumnProp>( taskTableColumnProps.filter((prop) => !initiallyHiddenColumnProps.includes(prop)),);
const allToolbarColumns = [ { prop: 'status', label: 'Status' }, { prop: 'name', label: 'Task' }, { prop: 'wbs', label: 'WBS' },] satisfies Array<Omit<GanttToolbarColumnOption, 'visible'>>;
export const toolbarColumns: GanttToolbarColumnOption[] = [...allToolbarColumns] .sort((a, b) => a.label.localeCompare(b.label, undefined, { sensitivity: 'base' })) .map((column) => ({ ...column, visible: initiallyVisibleColumnProps.has(column.prop as TaskTableColumnProp), }));
export const calendars: CalendarEntity[] = [ { id: CALENDAR_ID, name: 'Standard', timeZone: 'UTC', workingDays: [1, 2, 3, 4, 5], holidays: [], hoursPerDay: 8, },];
export const tasks: GanttTaskSourceRow[] = [ { id: 't1', parentId: null, name: 'Discovery', type: 'summary', workflowStatus: 'done', startDate: '2026-04-06', endDate: '2026-04-17', duration: 10, percentDone: 100, calendarId: CALENDAR_ID, tags: [], }, { id: 't2', parentId: 't1', name: 'Stakeholder Interviews', type: 'task', workflowStatus: 'done', startDate: '2026-04-06', endDate: '2026-04-08', duration: 3, percentDone: 100, calendarId: CALENDAR_ID, tags: [], }, { id: 't3', parentId: 't1', name: 'Analytics Review', type: 'task', workflowStatus: 'done', startDate: '2026-04-07', endDate: '2026-04-10', duration: 4, percentDone: 100, calendarId: CALENDAR_ID, tags: [], }, { id: 't4', parentId: 't1', name: 'Content Inventory', type: 'task', workflowStatus: 'done', startDate: '2026-04-09', endDate: '2026-04-14', duration: 4, percentDone: 100, calendarId: CALENDAR_ID, tags: [], }, { id: 't5', parentId: 't1', name: 'Requirements Workshop', type: 'task', workflowStatus: 'done', startDate: '2026-04-13', endDate: '2026-04-16', duration: 4, percentDone: 100, calendarId: CALENDAR_ID, tags: [], }, { id: 't6', parentId: 't1', name: 'Discovery Sign-off', type: 'milestone', workflowStatus: 'done', startDate: '2026-04-17', endDate: '2026-04-17', duration: 0, percentDone: 100, calendarId: CALENDAR_ID, tags: ['milestone'], }, { id: 't7', parentId: null, name: 'Design', type: 'summary', workflowStatus: 'in-progress', startDate: '2026-04-20', endDate: '2026-05-08', duration: 15, percentDone: 55, calendarId: CALENDAR_ID, tags: [], }, { id: 't8', parentId: 't7', name: 'Information Architecture', type: 'task', workflowStatus: 'done', startDate: '2026-04-20', endDate: '2026-04-23', duration: 4, percentDone: 100, calendarId: CALENDAR_ID, tags: [], }, { id: 't9', parentId: 't7', name: 'Wireframes', type: 'task', workflowStatus: 'done', startDate: '2026-04-24', endDate: '2026-04-29', duration: 4, percentDone: 100, calendarId: CALENDAR_ID, tags: [], }, { id: 't10', parentId: 't7', name: 'Design System Updates', type: 'task', workflowStatus: 'in-progress', startDate: '2026-04-27', endDate: '2026-05-01', duration: 5, percentDone: 70, calendarId: CALENDAR_ID, tags: [], }, { id: 't11', parentId: 't7', name: 'Visual Design', type: 'task', workflowStatus: 'in-progress', startDate: '2026-04-30', endDate: '2026-05-07', duration: 6, percentDone: 45, calendarId: CALENDAR_ID, tags: [], }, { id: 't12', parentId: 't7', name: 'Prototype Review', type: 'task', workflowStatus: 'not-started', startDate: '2026-05-05', endDate: '2026-05-07', duration: 3, percentDone: 0, calendarId: CALENDAR_ID, tags: [], }, { id: 't13', parentId: 't7', name: 'Design Approval', type: 'milestone', workflowStatus: 'not-started', startDate: '2026-05-08', endDate: '2026-05-08', duration: 0, percentDone: 0, calendarId: CALENDAR_ID, tags: ['milestone'], }, { id: 't14', parentId: null, name: 'Development', type: 'summary', workflowStatus: 'not-started', startDate: '2026-05-11', endDate: '2026-06-05', duration: 20, percentDone: 0, calendarId: CALENDAR_ID, tags: [], }, { id: 't15', parentId: 't14', name: 'Frontend Shell', type: 'task', workflowStatus: 'not-started', startDate: '2026-05-11', endDate: '2026-05-15', duration: 5, percentDone: 0, calendarId: CALENDAR_ID, tags: [], }, { id: 't16', parentId: 't14', name: 'CMS Integration', type: 'task', workflowStatus: 'not-started', startDate: '2026-05-11', endDate: '2026-05-20', duration: 8, percentDone: 0, calendarId: CALENDAR_ID, tags: [], }, { id: 't17', parentId: 't14', name: 'Reusable Page Sections', type: 'task', workflowStatus: 'not-started', startDate: '2026-05-18', endDate: '2026-05-27', duration: 8, percentDone: 0, calendarId: CALENDAR_ID, tags: [], }, { id: 't18', parentId: 't14', name: 'Search Experience', type: 'task', workflowStatus: 'not-started', startDate: '2026-05-19', endDate: '2026-05-26', duration: 6, percentDone: 0, calendarId: CALENDAR_ID, tags: [], }, { id: 't19', parentId: 't14', name: 'Analytics Events', type: 'task', workflowStatus: 'not-started', startDate: '2026-05-25', endDate: '2026-05-29', duration: 5, percentDone: 0, calendarId: CALENDAR_ID, tags: [], }, { id: 't20', parentId: 't14', name: 'Accessibility Pass', type: 'task', workflowStatus: 'not-started', startDate: '2026-05-28', endDate: '2026-06-02', duration: 4, percentDone: 0, calendarId: CALENDAR_ID, tags: [], }, { id: 't21', parentId: 't14', name: 'Performance Tuning', type: 'task', workflowStatus: 'not-started', startDate: '2026-06-01', endDate: '2026-06-04', duration: 4, percentDone: 0, calendarId: CALENDAR_ID, tags: [], }, { id: 't22', parentId: 't14', name: 'Feature Complete', type: 'milestone', workflowStatus: 'not-started', startDate: '2026-06-05', endDate: '2026-06-05', duration: 0, percentDone: 0, calendarId: CALENDAR_ID, tags: ['milestone'], }, { id: 't23', parentId: null, name: 'QA and Release Prep', type: 'summary', workflowStatus: 'not-started', startDate: '2026-06-08', endDate: '2026-06-18', duration: 9, percentDone: 0, calendarId: CALENDAR_ID, tags: [], }, { id: 't24', parentId: 't23', name: 'Test Plan', type: 'task', workflowStatus: 'not-started', startDate: '2026-06-08', endDate: '2026-06-10', duration: 3, percentDone: 0, calendarId: CALENDAR_ID, tags: [], }, { id: 't25', parentId: 't23', name: 'Cross-browser QA', type: 'task', workflowStatus: 'not-started', startDate: '2026-06-10', endDate: '2026-06-15', duration: 4, percentDone: 0, calendarId: CALENDAR_ID, tags: [], }, { id: 't26', parentId: 't23', name: 'Content QA', type: 'task', workflowStatus: 'not-started', startDate: '2026-06-11', endDate: '2026-06-16', duration: 4, percentDone: 0, calendarId: CALENDAR_ID, tags: [], }, { id: 't27', parentId: 't23', name: 'Release Candidate', type: 'milestone', workflowStatus: 'not-started', startDate: '2026-06-18', endDate: '2026-06-18', duration: 0, percentDone: 0, calendarId: CALENDAR_ID, tags: ['milestone'], }, { id: 't28', parentId: null, name: 'Launch', type: 'task', workflowStatus: 'not-started', startDate: '2026-06-19', endDate: '2026-06-23', duration: 3, percentDone: 0, calendarId: CALENDAR_ID, tags: [], }, { id: 't29', parentId: null, name: 'Post-launch Review', type: 'milestone', workflowStatus: 'not-started', startDate: '2026-06-26', endDate: '2026-06-26', duration: 0, percentDone: 0, calendarId: CALENDAR_ID, tags: ['milestone'], },];
export const dependencies: DependencyEntity[] = [ { id: 'd1', predecessorTaskId: 't2', successorTaskId: 't5', type: 'finish-to-start', lagDays: 0 }, { id: 'd2', predecessorTaskId: 't3', successorTaskId: 't5', type: 'finish-to-start', lagDays: 0 }, { id: 'd3', predecessorTaskId: 't4', successorTaskId: 't6', type: 'finish-to-start', lagDays: 0 }, { id: 'd4', predecessorTaskId: 't5', successorTaskId: 't6', type: 'finish-to-start', lagDays: 0 }, { id: 'd5', predecessorTaskId: 't6', successorTaskId: 't8', type: 'finish-to-start', lagDays: 1 }, { id: 'd6', predecessorTaskId: 't8', successorTaskId: 't9', type: 'finish-to-start', lagDays: 0 }, { id: 'd7', predecessorTaskId: 't9', successorTaskId: 't11', type: 'finish-to-start', lagDays: 0 }, { id: 'd8', predecessorTaskId: 't10', successorTaskId: 't12', type: 'finish-to-start', lagDays: 0 }, { id: 'd9', predecessorTaskId: 't11', successorTaskId: 't13', type: 'finish-to-start', lagDays: 0 }, { id: 'd10', predecessorTaskId: 't12', successorTaskId: 't13', type: 'finish-to-start', lagDays: 0 }, { id: 'd11', predecessorTaskId: 't13', successorTaskId: 't15', type: 'finish-to-start', lagDays: 1 }, { id: 'd12', predecessorTaskId: 't15', successorTaskId: 't17', type: 'finish-to-start', lagDays: 0 }, { id: 'd13', predecessorTaskId: 't16', successorTaskId: 't18', type: 'finish-to-start', lagDays: 0 }, { id: 'd14', predecessorTaskId: 't17', successorTaskId: 't20', type: 'finish-to-start', lagDays: 0 }, { id: 'd15', predecessorTaskId: 't18', successorTaskId: 't19', type: 'finish-to-start', lagDays: 0 }, { id: 'd16', predecessorTaskId: 't19', successorTaskId: 't21', type: 'finish-to-start', lagDays: 0 }, { id: 'd17', predecessorTaskId: 't20', successorTaskId: 't21', type: 'finish-to-start', lagDays: 0 }, { id: 'd18', predecessorTaskId: 't21', successorTaskId: 't22', type: 'finish-to-start', lagDays: 0 }, { id: 'd19', predecessorTaskId: 't22', successorTaskId: 't24', type: 'finish-to-start', lagDays: 1 }, { id: 'd20', predecessorTaskId: 't24', successorTaskId: 't25', type: 'finish-to-start', lagDays: 0 }, { id: 'd21', predecessorTaskId: 't24', successorTaskId: 't26', type: 'finish-to-start', lagDays: 0 }, { id: 'd22', predecessorTaskId: 't25', successorTaskId: 't27', type: 'finish-to-start', lagDays: 0 }, { id: 'd23', predecessorTaskId: 't26', successorTaskId: 't27', type: 'finish-to-start', lagDays: 0 }, { id: 'd24', predecessorTaskId: 't27', successorTaskId: 't28', type: 'finish-to-start', lagDays: 0 }, { id: 'd25', predecessorTaskId: 't28', successorTaskId: 't29', type: 'finish-to-start', lagDays: 2 },];<template> <section class="gantt-basic-demo"> <RevoGrid class="gantt-basic-grid" hide-attribution :can-move-columns="true" :theme="isDark ? 'darkCompact' : 'compact'" :plugins="plugins" :source="tasks" :columns="columns" :hide-columns.prop="hiddenColumns" :gantt.prop="ganttConfig" :gantt-dependencies.prop="dependencies" :gantt-calendars.prop="calendars" /> </section></template>
<script setup lang="ts">import { ref } from 'vue';import RevoGrid from '@revolist/vue3-datagrid';import { GanttPlugin, createDefaultTaskTableColumn } from '@revolist/gantt';import { currentThemeVue } from '../composables/useRandomData';import { calendars as basicCalendars, dependencies as basicDependencies, ganttConfig as basicGanttConfig, initiallyHiddenColumnProps, tasks as basicTasks, taskTableColumnProps } from './gantt-basic-data';import './gantt-basic.css';
const { isDark } = currentThemeVue();
const plugins = [GanttPlugin];const ganttConfig = ref(basicGanttConfig);const calendars = ref(basicCalendars);const tasks = ref(basicTasks.map((task) => ({ ...task })));const dependencies = ref(basicDependencies);const hiddenColumns = ref([...initiallyHiddenColumnProps]);
const columns = ref(taskTableColumnProps.map((prop) => createDefaultTaskTableColumn(prop)));</script>import React, { useMemo } from 'react';import { RevoGrid } from '@revolist/react-datagrid';import { GanttPlugin, createDefaultTaskTableColumn } from '@revolist/gantt';import { currentTheme } from '../composables/useRandomData';import { calendars, dependencies, ganttConfig, initiallyHiddenColumnProps, tasks, taskTableColumnProps } from './gantt-basic-data';import './gantt-basic.css';
const { isDark } = currentTheme();const plugins = [GanttPlugin];
function GanttBasic() { const project = useMemo(() => ganttConfig, []); const source = useMemo(() => tasks.map((task) => ({ ...task })), []); const hiddenColumns = useMemo(() => [...initiallyHiddenColumnProps], []); const columns = useMemo( () => taskTableColumnProps.map((prop) => createDefaultTaskTableColumn(prop)), [], );
return ( <section className="gantt-basic-demo"> <RevoGrid className="gantt-basic-grid" theme={isDark() ? 'darkCompact' : 'compact'} hideAttribution canMoveColumns plugins={plugins} source={source} columns={columns} hideColumns={hiddenColumns} gantt={project} ganttDependencies={dependencies} ganttCalendars={calendars} /> </section> );}
export default GanttBasic;import { Component, NO_ERRORS_SCHEMA, ViewEncapsulation } from '@angular/core';import { RevoGrid } from '@revolist/angular-datagrid';import { GanttPlugin, createDefaultTaskTableColumn } from '@revolist/gantt';import { currentTheme } from '../composables/useRandomData';import { calendars as basicCalendars, dependencies as basicDependencies, ganttConfig as basicGanttConfig, initiallyHiddenColumnProps, tasks as basicTasks, taskTableColumnProps } from './gantt-basic-data';
@Component({ selector: 'gantt-basic-grid', standalone: true, // Allows Angular demos to bind RevoGrid plugin props that are not wrapper inputs. schemas: [NO_ERRORS_SCHEMA], imports: [RevoGrid], template: ` <section class="gantt-basic-demo"> <revo-grid class="gantt-basic-grid" [theme]="theme" [hideAttribution]="true" [canMoveColumns]="true" [plugins]="plugins" [source]="tasks" [columns]="columns" [hideColumns]="hiddenColumns" [gantt]="ganttConfig" [ganttDependencies]="dependencies" [ganttCalendars]="calendars" ></revo-grid> </section> `, styleUrls: ['./gantt-basic.css'], encapsulation: ViewEncapsulation.None,})export class GanttBasicGridComponent { theme = currentTheme().isDark() ? 'darkCompact' : 'compact'; plugins = [GanttPlugin];
ganttConfig = basicGanttConfig; calendars = basicCalendars; tasks = basicTasks.map((task) => ({ ...task })); dependencies = basicDependencies; hiddenColumns = [...initiallyHiddenColumnProps];
columns = taskTableColumnProps.map((prop) => createDefaultTaskTableColumn(prop));}The scheduling data model is framework-neutral, but the Gantt grid renders in the browser. Register custom elements, access document or window, and mount the grid only on the client.
Integration Rules
Section titled “Integration Rules”- Render a stable-height placeholder during SSR to avoid layout shift.
- Dynamically import the wrapper or Gantt component with SSR disabled.
- Call
defineCustomElements()only in a browser entry or client lifecycle. - Keep project data serializable across the server/client boundary.
- Do not create the grid during module evaluation in code that also runs on the server.
- Clean up listeners and element instances when client-side routes unmount.
if (typeof window !== 'undefined') { const { defineCustomElements } = await import('@revolist/revogrid/loader'); defineCustomElements();}For React meta-frameworks, dynamically load the component client-side. For Vue/Nuxt use a client-only boundary. For Angular SSR, guard browser-only setup with the platform check used by your application.
The Vanilla quick start shows explicit mount and cleanup; the wrapper quick starts show property binding after hydration.
---import GanttBasic from '@revolist/revogrid-examples/components/gantt/GanttBasic.vue';---<div style="height: 600px"> <GanttBasic client:only="vue" /></div>Copy the component into your own application; the private workspace examples package is used only by this documentation site.