Timeline Views and Zoom
ApexGantt renders tasks on a scrollable horizontal timeline where the zoom level determines how many pixels represent one calendar day. That single number controls how much time fits in the visible area: a high value zooms in to show a few days in detail, a low value zooms out to show months or years at a glance.
View modes
ApexGantt ships five named view modes. Each mode is a shorthand for a specific pixelsPerDay value and drives which calendar tiers appear in the timeline header.
| Mode | viewMode value | Approximate time on screen | Typical use |
|---|---|---|---|
| Day | 'day' | 1-2 weeks | Detailed scheduling, hour-level work |
| Week | 'week' | 1-3 months | Sprint planning, short projects |
| Month | 'month' | 3-6 months | Most projects (default) |
| Quarter | 'quarter' | 1-2 years | Roadmaps, long-term planning |
| Year | 'year' | Multi-year | Executive overviews |
The ViewMode type is a string union: 'day' | 'week' | 'month' | 'quarter' | 'year'.
Setting viewMode at construction
Pass viewMode in the options object. If omitted, it defaults to 'month'.
import ApexGantt from 'apexgantt'
ApexGantt.setLicense('YOUR-LICENSE-KEY')
const gantt = new ApexGantt(document.getElementById('gantt'), {
series: [
{ id: '1', name: 'Discovery', startTime: '2024-04-01', endTime: '2024-04-14' },
{ id: '2', name: 'Design', startTime: '2024-04-15', endTime: '2024-04-30' },
{ id: '3', name: 'Development', startTime: '2024-05-01', endTime: '2024-06-15' },
{ id: '4', name: 'Testing', startTime: '2024-06-10', endTime: '2024-06-30' },
],
viewMode: 'week',
})
gantt.render()
Switching viewMode at runtime
Call update() with the new viewMode. The timeline re-renders immediately without discarding task data.
// Switch to a quarterly overview
gantt.update({ viewMode: 'quarter' })
// Switch back to week-level detail
gantt.update({ viewMode: 'week' })
update() accepts a partial options object, so you can change only viewMode while leaving all other configuration untouched.
Auto-fit zoom
When you omit both viewMode and pixelsPerDay, ApexGantt calculates a pixelsPerDay value that fits the full span of your task data into the available container width on first render. This is the default behavior. It is useful when you do not know the date range of the data ahead of time and want the chart to always start fully visible without horizontal scrolling.
Once the user scrolls or a programmatic zoom is applied, the auto-fit value is no longer re-calculated.
Fine-grained zoom with pixelsPerDay
viewMode is a convenience. Under the hood, ApexGantt works entirely with pixelsPerDay. Setting it directly gives you continuous control: any value between roughly 0.25 and 1280 is accepted.
When pixelsPerDay is set explicitly, it overrides the viewMode preset. The timeline header automatically selects the appropriate calendar tiers (year, quarter, month, week, day, halfday, hour, minute) based on the current value.
Calibrated reference values that correspond to each named view:
| Named view | Approximate pixelsPerDay |
|---|---|
'year' | 0.5 |
'quarter' | 1.6 |
'month' | 4.9 |
'week' | 25.7 |
'day' | 80 |
// Start at a custom density between 'week' and 'day'
const gantt = new ApexGantt(document.getElementById('gantt'), {
series: tasks,
pixelsPerDay: 40,
})
gantt.render()
You can also update it at runtime:
gantt.update({ pixelsPerDay: 25.7 }) // equivalent to week view
Programmatic zoom buttons
zoomIn() and zoomOut() step through the named zoom levels. Each call moves one level in the direction indicated. The underlying pixelsPerDay is updated and the header tiers adjust automatically.
zoomIn()moves toward finer detail: Year → Quarter → Month → Week → Day. CallingzoomIn()when already at Day has no effect.zoomOut()moves toward coarser overview: Day → Week → Month → Quarter → Year. CallingzoomOut()when already at Year has no effect.
A minimal toolbar using plain buttons:
<div id="gantt" style="height: 400px;"></div>
<div class="zoom-controls">
<button id="zoom-in">Zoom in</button>
<button id="zoom-out">Zoom out</button>
</div>
import ApexGantt from 'apexgantt'
ApexGantt.setLicense('YOUR-LICENSE-KEY')
const gantt = new ApexGantt(document.getElementById('gantt'), {
series: [
{ id: '1', name: 'Planning', startTime: '2024-01-01', endTime: '2024-03-31' },
{ id: '2', name: 'Execution', startTime: '2024-04-01', endTime: '2024-09-30' },
{ id: '3', name: 'Review', startTime: '2024-10-01', endTime: '2024-12-31' },
],
viewMode: 'month',
})
gantt.render()
document.querySelector('#zoom-in').addEventListener('click', () => gantt.zoomIn())
document.querySelector('#zoom-out').addEventListener('click', () => gantt.zoomOut())
The built-in toolbar also includes zoom controls automatically. If you prefer to rely on those instead of custom buttons, set toolbarItems to include the zoom items or leave the toolbar at its default configuration.
React
In React, bind viewMode to component state. The react-apexgantt wrapper diffs props on every render and calls update() internally only when a prop changes, so you do not need to manage the chart instance manually.
import { useState } from 'react'
import { ApexGanttChart } from 'react-apexgantt'
import type { ViewMode } from 'react-apexgantt'
const tasks = [
{ id: '1', name: 'Planning', startTime: '2024-01-01', endTime: '2024-03-31' },
{ id: '2', name: 'Execution', startTime: '2024-04-01', endTime: '2024-09-30' },
{ id: '3', name: 'Review', startTime: '2024-10-01', endTime: '2024-12-31' },
]
export default function GanttWithViewSelector() {
const [viewMode, setViewMode] = useState<ViewMode>('month')
return (
<div>
<div style={{ marginBottom: 12, display: 'flex', gap: 8 }}>
{(['day', 'week', 'month', 'quarter', 'year'] as ViewMode[]).map((m) => (
<button
key={m}
style={{ fontWeight: viewMode === m ? 'bold' : 'normal' }}
onClick={() => setViewMode(m)}
>
{m.charAt(0).toUpperCase() + m.slice(1)}
</button>
))}
</div>
<ApexGanttChart tasks={tasks} viewMode={viewMode} height="400px" />
</div>
)
}
To call zoomIn() and zoomOut() from buttons in React, use the imperative ref API. See React ApexGantt for the full ApexGanttHandle reference.
import { useRef } from 'react'
import { ApexGanttChart } from 'react-apexgantt'
import type { ApexGanttHandle } from 'react-apexgantt'
export default function GanttWithZoom() {
const ganttRef = useRef<ApexGanttHandle>(null)
return (
<div>
<div style={{ marginBottom: 12, display: 'flex', gap: 8 }}>
<button onClick={() => ganttRef.current?.zoomIn()}>Zoom in</button>
<button onClick={() => ganttRef.current?.zoomOut()}>Zoom out</button>
</div>
<ApexGanttChart ref={ganttRef} tasks={tasks} height="400px" />
</div>
)
}
Vue
In Vue, bind view-mode to a ref. The vue-apexgantt wrapper handles the update() call when the prop changes.
<template>
<div>
<div style="margin-bottom: 12px; display: flex; gap: 8px;">
<button
v-for="m in viewModes"
:key="m"
:style="{ fontWeight: viewMode === m ? 'bold' : 'normal' }"
@click="viewMode = m"
>
{{ m.charAt(0).toUpperCase() + m.slice(1) }}
</button>
</div>
<ApexGanttChart :tasks="tasks" :view-mode="viewMode" height="400px" />
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { ApexGanttChart } from 'vue-apexgantt'
import type { ViewMode } from 'vue-apexgantt'
const viewModes: ViewMode[] = ['day', 'week', 'month', 'quarter', 'year']
const viewMode = ref<ViewMode>('month')
const tasks = [
{ id: '1', name: 'Planning', startTime: '2024-01-01', endTime: '2024-03-31' },
{ id: '2', name: 'Execution', startTime: '2024-04-01', endTime: '2024-09-30' },
{ id: '3', name: 'Review', startTime: '2024-10-01', endTime: '2024-12-31' },
]
</script>
To call zoomIn() and zoomOut() from Vue, use a template ref. See Vue ApexGantt for the full template ref method list.
Crosshair
The crosshair is a vertical line that tracks the cursor across the timeline. It shows a date label at the top of the timeline as the user moves the mouse, making it easy to read off exact dates on dense charts.
Enable it with enableCrosshair: true:
const gantt = new ApexGantt(document.getElementById('gantt'), {
series: tasks,
enableCrosshair: true,
})
gantt.render()
Crosshair color
Change the line color with crosshairColor. Any CSS color value is accepted.
const gantt = new ApexGantt(document.getElementById('gantt'), {
series: tasks,
enableCrosshair: true,
crosshairColor: '#F59E0B',
})
gantt.render()
Custom crosshair label
The crosshairLabelFormat callback lets you control the text shown in the date label. It receives the Date under the cursor and a tier string that describes which calendar resolution is currently active at the cursor position.
const gantt = new ApexGantt(document.getElementById('gantt'), {
series: tasks,
enableCrosshair: true,
crosshairLabelFormat: (date, tier) => {
// tier: 'minute' | 'hour' | 'halfday' | 'day' | 'week' | 'month' | 'quarter' | 'year'
if (tier === 'day' || tier === 'halfday') {
return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' })
}
if (tier === 'hour' || tier === 'minute') {
return date.toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit' })
}
return date.toLocaleDateString('en-US', { month: 'short', year: 'numeric' })
},
})
gantt.render()
The tier value matches the current header resolution, so the label can stay contextually relevant as the user zooms in and out.
Snap behavior
When a user drags or resizes a task bar, ApexGantt snaps the task boundaries to the nearest unit. This prevents tasks from landing on arbitrary timestamps and keeps the schedule clean.
snapUnit sets the unit of the grid: 'day', 'hour', or 'minute'.
snapValue sets how many of those units form one snap interval. The default is 1, meaning every day (or hour, or minute). Set it higher to enforce coarser alignment.
const gantt = new ApexGantt(document.getElementById('gantt'), {
series: tasks,
// Snap to the nearest half-day boundary
snapUnit: 'hour',
snapValue: 12,
})
gantt.render()
Common patterns:
// Daily snap (default)
{ snapUnit: 'day', snapValue: 1 }
// Weekly snap: tasks align to week boundaries
{ snapUnit: 'day', snapValue: 7 }
// Two-hour snap for sub-day scheduling
{ snapUnit: 'hour', snapValue: 2 }
// 15-minute snap for fine scheduling
{ snapUnit: 'minute', snapValue: 15 }
Snap only applies when enableTaskDrag or enableTaskResize is true. If both are false, the snap settings have no visible effect.
Related
- Options reference for all timeline and toolbar options
- Methods reference for
zoomIn,zoomOut, andupdate - React ApexGantt for the
viewModeprop andApexGanttHandle - Vue ApexGantt for the
view-modeprop and template ref methods