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
ColumnKey | key string | Default shown | Description |
|---|---|---|---|
Name | 'name' | Yes | Task name |
StartTime | 'startTime' | Yes | Start date |
EndTime | 'endTime' | Yes | End date |
Duration | 'duration' | No | Computed working-day duration |
Progress | 'progress' | No | Completion percent as text |
ProgressRing | 'progressRing' | No | Circular progress indicator |
Wbs | 'wbs' | No | Work Breakdown Structure code |
Predecessors | 'predecessors' | No | Tasks this task depends on, derived from the dependency graph |
Successors | 'successors' | No | Tasks that depend on this task, derived from the dependency graph |
Assignees | 'assignees' | No | Assignee names from the task's assignees array |
BaselineStart | 'baselineStart' | No | Planned start date from the task's baseline |
BaselineEnd | 'baselineEnd' | No | Planned end date from the task's baseline |
BaselineVariance | 'baselineVariance' | No | Difference 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
| Property | Type | Description |
|---|---|---|
key | ColumnKey | string | Built-in key or any string for a custom column |
title | string | Column header text |
minWidth | string | CSS minimum width (e.g. '120px'); the preferred width when auto-sizing is on |
maxWidth | string | Ceiling for the auto-sized width (default '320px'); ignored when auto-sizing is off |
flexGrow | number | Flex grow ratio; controls how extra space is distributed |
visible | boolean | When false, the column is hidden |
resizable | boolean | When false, locks the column at its auto/configured width (default true) |
render | ColumnRenderer | Custom cell renderer (required for custom columns) |
accessor | (task) => unknown | Extracts the cell's underlying value, used by sorting, filtering, and SVG export |
sortable | boolean | Whether the column participates in sorting; custom columns are sortable only with an accessor or comparator |
comparator | (a, b) => number | Custom 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
| Option | Type | Default | Description |
|---|---|---|---|
accessor | (task) => Assignee[] | Required | Returns the list of assignees for a task |
max | number | 4 | Max avatars shown; excess shown as +N |
size | number | 24 | Avatar diameter in pixels |
overlap | number | 8 | Overlap between adjacent avatars in pixels |
borderColor | string | '#FFFFFF' | Border around each avatar |
fallbackColor | string | — | Background for initials fallback when Assignee.color is not set |
Assignee fields
| Field | Type | Description |
|---|---|---|
name | string | Display name; used for alt text and initials source |
avatarUrl | string | Image URL — when set, renders an <img> |
initials | string | Override for the two-letter initials fallback |
color | string | Background 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>