Docyrus

Gantt

Gantt chart with grouped features, lanes, drag-to-move and edge resize, long-press add, date markers, weekend shading, and zoom control.

iOSAndroid
Preview Gantt on your device

Scan with Expo Go

Download Expo Go, then scan the QR code to preview native components.

Installation

pnpm dlx @docyrus/cli add @docyrus/rn-gantt
Required Packages(3 packages)
pnpm add react-native-reanimated react-native-gesture-handler date-fns

Usage

import { GanttView, type GanttGroup } from '@/components/docyrus-native/gantt';

const groups: GanttGroup[] = [
  {
    id: 'frontend',
    name: 'Frontend',
    features: [
      {
        id: 'f1',
        name: 'Auth Flow',
        startAt: new Date(2026, 2, 1),
        endAt: new Date(2026, 2, 15),
        status: { id: 'done', name: 'Done', color: '#22c55e' }
      }
    ]
  }
];

<GanttView
  groups={groups}
  range="monthly"
  showToday
  onFeatureMove={(id, startAt, endAt) => save(id, startAt, endAt)}
  onFeaturePress={(feature) => console.log(feature.name)} />

GanttView is a monolithic adaptation of the web compound Gantt (GanttProvider, GanttSidebar*, GanttFeatureRow, GanttAddFeatureHelper, GanttCreateMarkerTrigger, …). The compound pieces are too granular for React Native, so the same capabilities are exposed as props and render props on one component.

Lanes

Features of the same group that share a lane are packed into one row — overlapping features stack into sub-rows, exactly like web GanttFeatureRow. Features without a lane get a row of their own. The sidebar shows a lane row with the lane name, its item count, and the duration of the whole lane.

const features = [
  { id: 'b1', name: 'API', startAt, endAt, status, lane: 'Sprint work' },
  { id: 'b2', name: 'Migration', startAt: later, endAt: evenLater, status, lane: 'Sprint work' }
];

Adding items and markers

Hover affordances from web become long-presses:

  • Long-press an empty timeline cell → onAddItem(date, info) (web GanttAddFeatureHelper). info carries the pressed group and row (so you can put the new feature into the same lane). Presses that land on a bar are ignored.
  • Long-press the timeline header → onCreateMarker(date) (web GanttCreateMarkerTrigger).
  • Tap a marker label → an action sheet with Remove marker → onMarkerRemove(id) (web right-click context menu). Without onMarkerRemove the label is not interactive.
<GanttView
  groups={groups}
  markers={markers}
  onAddItem={(date, { group, row }) => createFeature(group?.id, date, row?.lane)}
  onCreateMarker={(date) => setMarkers(m => [...m, { id: uid(), date, label: 'Milestone' }])}
  onMarkerRemove={(id) => setMarkers(m => m.filter(x => x.id !== id))} />

Custom content

<GanttView
  groups={groups}
  renderFeature={(feature) => (
    <Text className="text-[10px] font-semibold text-white">{feature.name} · {feature.status.name}</Text>
  )}
  renderSidebarItem={(row) => (
    <Text className="text-xs text-foreground">{row.lane ?? row.features[0]?.name}</Text>
  )} />

Interactions

GestureResult
Tap a baronFeaturePress(feature)
Long-press (200 ms) + drag a bar horizontallyLive preview, then onFeatureMove(id, startAt, endAt) (duration kept)
Long-press (100 ms) + drag a bar's left / right edgeLive resize preview, then onFeatureMove with the new start / end
Tap a sidebar rowScrolls to the (earliest) feature, then onSelectItem(id) + onFeaturePress(feature)
Long-press an empty cellonAddItem(date, info)
Long-press the headeronCreateMarker(date)
Tap a marker labelRemove-marker action sheet → onMarkerRemove(id)

Drag and resize are enabled only when onFeatureMove is supplied and readOnly is false. readOnly also disables long-press add and marker create / remove.

API Reference

PropTypeDefaultDescription
groupsGanttGroup[]—Feature groups (required)
range'daily' | 'monthly' | 'quarterly''monthly'Timeline range mode
zoomnumber100Zoom level percentage (scales the column width)
markersGanttMarkerData[]—Date markers on the timeline
onFeatureMove(id: string, startAt: Date, endAt: Date) => void—Drag-to-move / edge-resize callback. Enables drag + resize
onFeaturePress(feature: GanttFeature) => void—Feature bar or sidebar row tap
onSelectItem(id: string) => void—Sidebar row tap — feature id (a lane row passes its earliest feature)
onAddItem(date: Date, info: GanttAddItemInfo) => void—Long-press an empty timeline cell
onCreateMarker(date: Date) => void—Long-press the timeline header
onMarkerRemove(id: string) => void—Remove-marker action (tap a marker label)
renderFeature(feature: GanttFeature) => ReactNode—Custom content inside each feature bar (replaces the name label)
renderSidebarItem(row: GanttRowData, group: GanttGroup) => ReactNode—Custom sidebar row content (replaces dot + name + duration)
isColumnSecondary(date: Date) => booleanweekendsDaily range only — shades matching day columns
readOnlybooleanfalseDisables drag, resize, long-press add, and marker create / remove
showTodaybooleantrueShow the today line
classNamestring—Additional classes for the root view
styleViewStyle—Style override for the root view

GanttGroup

FieldTypeDescription
idstringUnique group identifier
namestringGroup display name
featuresGanttFeature[]Features in this group

GanttFeature

FieldTypeDescription
idstringUnique feature identifier
namestringFeature display name
startAtDateStart date
endAtDateEnd date
statusGanttStatusStatus with id, name, and color (bar color)
lanestringOptional lane id — features sharing a lane share one row

GanttStatus

FieldTypeDescription
idstringStatus identifier
namestringStatus display name
colorstringStatus bar color

GanttMarkerData

FieldTypeDescription
idstringMarker identifier
dateDateMarker date
labelstringMarker label
colorstringMarker line color (optional, defaults to primary)

GanttRowData

FieldTypeDescription
idstringfeature.id for a lane-less row, lane:<lane> for a lane row
lanestringLane id when the row is a lane row
featuresGanttFeature[]Row features, sorted by startAt

GanttAddItemInfo

FieldTypeDescription
groupGanttGroupGroup under the finger (undefined below the last group)
rowGanttRowDataRow under the finger (undefined on a group header row)

Translations

KeyFallback
ui.gantt.issuesIssues
ui.gantt.durationDuration
ui.gantt.itemCount{count} items
ui.gantt.removeMarkerRemove marker
ui.common.cancelCancel

Durations use date-fns formatDistance (same as web). Marker dates use useDateFormat().formatDate when a DateFormatProvider is mounted, else MMM dd, yyyy.

Components

ComponentDescription
GanttViewMain Gantt chart component

Type Exports

TypeDescription
GanttViewPropsProps for GanttView
GanttGroupGroup with features
GanttFeatureIndividual timeline feature
GanttStatusFeature status definition
GanttMarkerDataTimeline date marker
GanttMarkerPropsWeb-parity alias of GanttMarkerData
GanttRowDataA rendered row (single feature or lane)
GanttAddItemInfoSecond argument of onAddItem
GanttRangeRange mode type
RangeWeb-parity alias of GanttRange

On this page