Row Grouping and Hierarchy

ApexGantt supports multi-level task hierarchies through parentId. Parent rows automatically compute their date span from their children and display as summary (group) bars. This page covers how to structure hierarchical data, control the summary bar appearance, and use rollup markers.

Building a hierarchy with parentId

Set parentId on a task to nest it under another task. The parent task displays a summary bar spanning the earliest child start to the latest child end:

const tasks = [
  {
    id: 'phase-1',
    name: 'Phase 1: Research',
    startTime: '2024-03-01',
    endTime: '2024-03-31',
  },
  {
    id: 'task-1-1',
    name: 'User Interviews',
    startTime: '2024-03-01',
    endTime: '2024-03-10',
    progress: 100,
    parentId: 'phase-1',
  },
  {
    id: 'task-1-2',
    name: 'Competitive Analysis',
    startTime: '2024-03-11',
    endTime: '2024-03-20',
    progress: 80,
    parentId: 'phase-1',
  },
  {
    id: 'task-1-3',
    name: 'Requirements Doc',
    startTime: '2024-03-21',
    endTime: '2024-03-31',
    progress: 40,
    parentId: 'phase-1',
  },
  {
    id: 'phase-2',
    name: 'Phase 2: Design',
    startTime: '2024-04-01',
    endTime: '2024-04-30',
    dependency: 'phase-1',
  },
  {
    id: 'task-2-1',
    name: 'Wireframes',
    startTime: '2024-04-01',
    endTime: '2024-04-15',
    progress: 20,
    parentId: 'phase-2',
  },
]

const gantt = new ApexGantt(document.getElementById('chart'), { series: tasks })

Hierarchy depth is unlimited. Any task can be both a child of one task and a parent of others.

Summary bars

A parent task renders its own data (startTime, endTime, progress) as the bar by default. Set showSummaryBar: true on a parent to have its dates recomputed automatically from its descendants instead:

{
  id: 'phase-1',
  name: 'Phase 1: Research',
  startTime: '2024-03-01',   // ignored when showSummaryBar: true
  endTime: '2024-03-31',     // ignored when showSummaryBar: true
  showSummaryBar: true,      // dates computed from children
},

With showSummaryBar: true, drag, resize, and progress editing are disabled on the parent row — it is always read-only and recomputes whenever a child changes.

The summary bar uses summaryBarColor for its fill:

const gantt = new ApexGantt(el, {
  series: tasks,
  summaryBarColor: '#475569',   // default is a dark gray
})

Collapsed state

Set collapsed: true on a parent task to start with its children hidden. The user can expand/collapse interactively:

{
  id: 'phase-2',
  name: 'Phase 2: Design',
  startTime: '2024-04-01',
  endTime: '2024-04-30',
  showSummaryBar: true,
  collapsed: true,   // children hidden on initial render
},

collapsed is a mutable field — the chart updates it internally as the user expands and collapses rows without needing an external re-render.

Rollup markers

enableRollups: true draws small thin markers below each summary bar at the date position of each leaf descendant. The markers remain visible even when the parent is collapsed, giving a compact overview of the subtree schedule without expanding it:

const gantt = new ApexGantt(el, {
  series: tasks,
  enableRollups: true,
})

WBS codes

When tasks are arranged in a hierarchy, ApexGantt computes Work Breakdown Structure codes automatically (e.g. '1', '1.1', '1.2', '2', '2.1.1'). Show them in the task list by adding the wbs column:

columnConfig: [
  { key: 'wbs',       title: '#',     minWidth: '60px',  flexGrow: 0 },
  { key: 'name',      title: 'Task',  minWidth: '180px', flexGrow: 3 },
  { key: 'startTime', title: 'Start', minWidth: '100px', flexGrow: 1 },
  { key: 'endTime',   title: 'End',   minWidth: '100px', flexGrow: 1 },
],

WBS codes are recomputed whenever the tree structure changes (add, delete, move, indent, outdent).

Adding child tasks programmatically

Pass parentId in the second argument to addTask():

gantt.addTask(
  {
    id: 'task-new',
    name: 'New subtask',
    startTime: '2024-04-16',
    endTime: '2024-04-20',
  },
  { parentId: 'phase-2' },
)

Moving tasks between parents

// promote to root
gantt.moveTask('task-1-1', { newParentId: undefined })

// reparent to a different group
gantt.moveTask('task-1-1', { newParentId: 'phase-2' })

moveTask() recomputes WBS codes, summary dates (when showSummaryBar: true), and critical path markers automatically.

Full example

const gantt = new ApexGantt(document.getElementById('chart'), {
  series: [
    {
      id: 'phase-1',
      name: 'Phase 1',
      startTime: '2024-03-01',
      endTime: '2024-03-31',
      showSummaryBar: true,
    },
    {
      id: 'task-1-1',
      name: 'Research',
      startTime: '2024-03-01',
      endTime: '2024-03-15',
      progress: 100,
      parentId: 'phase-1',
    },
    {
      id: 'task-1-2',
      name: 'Spec Writing',
      startTime: '2024-03-16',
      endTime: '2024-03-31',
      progress: 60,
      parentId: 'phase-1',
    },
    {
      id: 'phase-2',
      name: 'Phase 2',
      startTime: '2024-04-01',
      endTime: '2024-04-30',
      showSummaryBar: true,
      collapsed: true,
      dependency: 'phase-1',
    },
    {
      id: 'task-2-1',
      name: 'Implementation',
      startTime: '2024-04-01',
      endTime: '2024-04-25',
      progress: 0,
      parentId: 'phase-2',
    },
  ],
  summaryBarColor: '#334155',
  enableRollups: true,
  columnConfig: [
    { key: 'wbs',       title: '#',     minWidth: '60px',  flexGrow: 0 },
    { key: 'name',      title: 'Task',  minWidth: '180px', flexGrow: 3 },
    { key: 'startTime', title: 'Start', minWidth: '100px', flexGrow: 1 },
    { key: 'endTime',   title: 'End',   minWidth: '100px', flexGrow: 1 },
    { key: 'progress',  title: '%',     minWidth: '60px',  flexGrow: 0 },
  ],
})

React example

import { ApexGanttChart } from 'react-apexgantt'
import type { TaskInput, GanttUserOptions } from 'react-apexgantt'

const tasks: TaskInput[] = [
  { id: 'p1',     name: 'Phase 1',      startTime: '2024-03-01', endTime: '2024-03-31', showSummaryBar: true },
  { id: 't1',     name: 'Research',     startTime: '2024-03-01', endTime: '2024-03-15', progress: 100, parentId: 'p1' },
  { id: 't2',     name: 'Spec Writing', startTime: '2024-03-16', endTime: '2024-03-31', progress: 60,  parentId: 'p1' },
  { id: 'p2',     name: 'Phase 2',      startTime: '2024-04-01', endTime: '2024-04-30', showSummaryBar: true, dependency: 'p1' },
  { id: 't3',     name: 'Build',        startTime: '2024-04-01', endTime: '2024-04-25', progress: 0,   parentId: 'p2' },
]

const options: Omit<GanttUserOptions, 'series'> = {
  summaryBarColor: '#334155',
  enableRollups: true,
  columnConfig: [
    { key: 'wbs',  title: '#',    minWidth: '60px',  flexGrow: 0 },
    { key: 'name', title: 'Task', minWidth: '180px', flexGrow: 3 },
  ],
}

export default function HierarchyGantt() {
  return <ApexGanttChart tasks={tasks} options={options} viewMode="week" height="500px" />
}

Vue example

<template>
  <ApexGanttChart :tasks="tasks" :options="ganttOptions" view-mode="week" height="500px" />
</template>

<script setup lang="ts">
import type { GanttUserOptions, TaskInput } from 'vue-apexgantt'

const tasks: TaskInput[] = [
  { id: 'p1', name: 'Phase 1', startTime: '2024-03-01', endTime: '2024-03-31', showSummaryBar: true },
  { id: 't1', name: 'Research', startTime: '2024-03-01', endTime: '2024-03-15', progress: 100, parentId: 'p1' },
  { id: 't2', name: 'Design',   startTime: '2024-03-16', endTime: '2024-03-31', progress: 40,  parentId: 'p1' },
]

const ganttOptions: Partial<GanttUserOptions> = {
  summaryBarColor: '#334155',
  enableRollups: true,
}
</script>