Custom Task-List Columns

The task-list panel on the left side of the chart shows a configurable set of columns. Pass columnConfig to control which columns appear, in what order, and how wide they are. You can also add fully custom columns with a render function.

Built-in columns

ColumnKeykey stringDefault shownDescription
Name'name'YesTask name
StartTime'startTime'YesStart date
EndTime'endTime'YesEnd date
Duration'duration'NoComputed working-day duration
Progress'progress'NoCompletion percent as text
ProgressRing'progressRing'NoCircular progress indicator
Wbs'wbs'NoWork Breakdown Structure code
Predecessors'predecessors'NoTasks this task depends on, derived from the dependency graph
Successors'successors'NoTasks that depend on this task, derived from the dependency graph
Assignees'assignees'NoAssignee names from the task's assignees array
BaselineStart'baselineStart'NoPlanned start date from the task's baseline
BaselineEnd'baselineEnd'NoPlanned end date from the task's baseline
BaselineVariance'baselineVariance'NoDifference between actual and baseline dates

Predecessors and Successors refresh automatically as dependencies or the WBS change. Every built-in column except Wbs is sortable and filterable, and all are groupable.

Showing a subset of columns

const gantt = new ApexGantt(document.getElementById('chart'), {
  series: tasks,
  columnConfig: [
    { key: 'name',      title: 'Task',     minWidth: '200px', flexGrow: 3 },
    { key: 'startTime', title: 'Start',    minWidth: '100px', flexGrow: 1 },
    { key: 'endTime',   title: 'End',      minWidth: '100px', flexGrow: 1 },
    { key: 'progress',  title: 'Progress', minWidth: '80px',  flexGrow: 1 },
  ],
})

ColumnListItem properties

PropertyTypeDescription
keyColumnKey | stringBuilt-in key or any string for a custom column
titlestringColumn header text
minWidthstringCSS minimum width (e.g. '120px'); the preferred width when auto-sizing is on
maxWidthstringCeiling for the auto-sized width (default '320px'); ignored when auto-sizing is off
flexGrownumberFlex grow ratio; controls how extra space is distributed
visiblebooleanWhen false, the column is hidden
resizablebooleanWhen false, locks the column at its auto/configured width (default true)
renderColumnRendererCustom cell renderer (required for custom columns)
accessor(task) => unknownExtracts the cell's underlying value, used by sorting, filtering, and SVG export
sortablebooleanWhether the column participates in sorting; custom columns are sortable only with an accessor or comparator
comparator(a, b) => numberCustom sort comparator; takes precedence over accessor-based comparison

Adding WBS and duration

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

Auto-sizing, resizing, and reordering

The task-list panel behaves like a data grid. Three options, all on by default, control how columns are sized and arranged.

Auto-size columns

autoSizeColumns (default true) sizes each column to fit its header title and widest cell content, growing the panel so nothing is clipped. minWidth sets a column's preferred width and maxWidth (default '320px') caps how wide it grows. Dragging the panel divider still works: it distributes width proportionally when widening and shrinks toward a small floor when narrowing.

Set autoSizeColumns: false for the legacy behavior, where the panel width is split purely by flexGrow:

const gantt = new ApexGantt(el, {
  series: tasks,
  autoSizeColumns: false,
})

Resize a column

resizableColumns (default true) lets users drag the handle at a column header's trailing edge to pin that column to an exact pixel width; the other columns absorb the leftover space. Double-click a handle to reset a column to auto width. Lock a single column with columnConfig[].resizable: false.

Drive it programmatically:

gantt.setColumnWidth(ColumnKey.Name, 260)
gantt.resetColumnWidths(ColumnKey.Name)   // one column
gantt.resetColumnWidths()                 // all columns
gantt.getColumnWidths()                   // { name: 260, ... }

Every resize emits a columnResize event.

Reorder columns

reorderableColumns (default true) lets users drag a column header left or right onto another column to move it; a vertical drop indicator shows the insertion point. A short drag threshold keeps a plain header click sorting as usual.

gantt.setColumnOrder([ColumnKey.Name, ColumnKey.Progress, ColumnKey.StartTime])
gantt.getColumnOrder()   // ['name', 'progress', 'startTime', ...]

Every reorder emits a columnReorder event. Both manual widths and column order are captured by getState() / setState().

Sortable and filterable columns

Built-in value columns are sortable and filterable out of the box. A custom column becomes sortable when you supply an accessor (the value used for comparison) or a comparator (a full sort function). Set sortable: false to opt a column out of header-click sorting even when it has an accessor.

columnConfig: [
  { key: 'name', title: 'Task', minWidth: '180px' },
  {
    key: 'priority',
    title: 'Priority',
    minWidth: '90px',
    accessor: (task) => task.priority,                 // makes the column sortable + filterable
    comparator: (a, b) => a.priority - b.priority,     // optional: custom ordering
    render: (ctx) => `<span>${ctx.task.priority}</span>`,
  },
]

See the Sorting and Filtering guide for the full sort and filter API.

Custom column with a render function

The render function receives (ctx, el) where ctx is the render context and el is the pre-attached cell DOM element.

Return a string to have the library set it as innerHTML. Return void after mounting your own content into el. Return a cleanup function to unmount framework components when the cell is discarded.

const gantt = new ApexGantt(el, {
  series: tasks,
  columnConfig: [
    { key: 'name',     title: 'Task',   minWidth: '180px', flexGrow: 3 },
    { key: 'startTime',title: 'Start',  minWidth: '100px', flexGrow: 1 },
    {
      key: 'status',
      title: 'Status',
      minWidth: '100px',
      flexGrow: 1,
      render: (ctx) => {
        const { task } = ctx
        const label =
          task.progress === 100 ? 'Done' :
          task.progress > 0    ? 'In Progress' :
                                 'Not Started'
        const color =
          task.progress === 100 ? '#10B981' :
          task.progress > 0    ? '#F59E0B' :
                                 '#94A3B8'
        return `
          <span style="
            display:inline-block;padding:2px 8px;border-radius:10px;
            background:${color}22;color:${color};font-size:11px;font-weight:600;
          ">${label}</span>
        `
      },
      accessor: (task) => task.progress,
    },
  ],
})

Built-in avatars renderer

When tasks include an assignees array, use the built-in renderers.avatars factory to render stacked avatar chips without writing the DOM yourself:

import ApexGantt, { renderers } from 'apexgantt'

const gantt = new ApexGantt(el, {
  series: [
    {
      id: 'task-1',
      name: 'Design System',
      startTime: '2024-03-01',
      endTime: '2024-03-15',
      progress: 60,
      assignees: [
        { name: 'Alice Chen',  avatarUrl: '/avatars/alice.jpg' },
        { name: 'Bob Patel',   color: '#6366F1' },
        { name: 'Carol Smith', initials: 'CS', color: '#10B981' },
      ],
    },
  ],
  columnConfig: [
    { key: 'name',     title: 'Task',     minWidth: '180px', flexGrow: 3 },
    { key: 'startTime',title: 'Start',    minWidth: '100px', flexGrow: 1 },
    {
      key: 'assigned',
      title: 'Assigned',
      minWidth: '120px',
      flexGrow: 1,
      render: renderers.avatars({
        accessor: (task) => task.assignees,
        max: 3,         // show up to 3 avatars, then "+N"
        size: 24,       // diameter in px
        overlap: 8,     // how much each avatar overlaps the previous
      }),
    },
  ],
})

AvatarsRendererOptions

OptionTypeDefaultDescription
accessor(task) => Assignee[]RequiredReturns the list of assignees for a task
maxnumber4Max avatars shown; excess shown as +N
sizenumber24Avatar diameter in pixels
overlapnumber8Overlap between adjacent avatars in pixels
borderColorstring'#FFFFFF'Border around each avatar
fallbackColorstringBackground for initials fallback when Assignee.color is not set

Assignee fields

FieldTypeDescription
namestringDisplay name; used for alt text and initials source
avatarUrlstringImage URL — when set, renders an <img>
initialsstringOverride for the two-letter initials fallback
colorstringBackground color for the initials fallback

React example

import { ApexGanttChart } from 'react-apexgantt'
import { renderers } from 'apexgantt'
import type { GanttUserOptions } from 'react-apexgantt'

const options: Omit<GanttUserOptions, 'series'> = {
  columnConfig: [
    { 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: 'status',
      title: 'Status',
      minWidth: '90px',
      flexGrow: 1,
      render: (ctx) => {
        const pct = ctx.task.progress
        const label = pct === 100 ? 'Done' : pct > 0 ? 'In Progress' : 'Not Started'
        const color = pct === 100 ? '#10B981' : pct > 0 ? '#F59E0B' : '#94A3B8'
        return `<span style="color:${color};font-size:11px;font-weight:600">${label}</span>`
      },
    },
    {
      key: 'assigned',
      title: 'Team',
      minWidth: '110px',
      flexGrow: 1,
      render: renderers.avatars({ accessor: (task) => task.assignees }),
    },
  ],
}

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

Vue example

<template>
  <ApexGanttChart :tasks="tasks" :options="ganttOptions" height="500px" />
</template>

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

const ganttOptions: Partial<GanttUserOptions> = {
  columnConfig: [
    { 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: 'Progress',minWidth: '80px',  flexGrow: 1 },
    {
      key: 'assigned',
      title: 'Team',
      minWidth: '110px',
      flexGrow: 1,
      render: renderers.avatars({ accessor: (task) => task.assignees, max: 3 }),
    },
  ],
}
</script>