Ganttastic
1.3.0indexedInteractive Gantt chart visualization with customizable tasks, dependencies, and progress indicators. Includes tooltips for task information and plans for additional features like timeline zoom and dark mode.
Interactive Gantt chart visualization with customizable tasks, dependencies, and progress indicators. Includes tooltips for task information and plans for additional features like timeline zoom and dark mode.
A modern Gantt chart implementation using Compose Multiplatform, targeting Wasm/JS with Kotlin.

// In your build.gradle.kts
dependencies {
implementation("io.github.kotlinlabs:ganttly:$version")
}
fun main() {
CanvasBasedWindow("Ganttastic", canvasElementId = "ganttasticCanvas") {
val ganttState = remember { createSampleNestedGanttState() }
customTheme = ganttTheme {
naming {
taskListHeader =
taskGroups =
noGroupsMessage =
}
}
GanttChartView(
state = ganttState,
headerContent = {
Column(modifier = Modifier.fillMaxWidth().padding(dp)) {
Text(
,
style = MaterialTheme.typography.headlineMedium,
fontWeight = FontWeight.Bold
)
}
},
ganttTheme = customTheme
)
}
}
Ganttly provides a comprehensive theme system that allows you to customize colors, styles, typography, and naming.
val customTheme = ganttTheme {
styles {
// Task bar styling
taskBarHeight = 0.7f
taskBarCornerRadius = 0.25f
taskBarTextPadding = 4.dp
// Dependency arrow styling
dependencyArrowWidth = .dp
dependencyArrowHeadSize = dp
dependencyArrowCornerRadius = dp
rowBorderWidth = .dp
timelineHeaderBorderWidth = .dp
groupTagShape =
groupTagBorderWidth = dp
groupTagPadding = dp
showTaskProgress =
}
}
val customTheme = ganttTheme {
typography {
// Task bar text
taskBarTextStyle = TextStyle(
fontFamily = FontFamily.SansSerif,
fontSize = 12.sp
)
// Timeline header text
timelineHeaderTextStyle = TextStyle(
fontFamily = FontFamily.Monospace,
fontSize = 14.sp,
fontWeight = FontWeight.Bold
)
// Tooltip text
tooltipTitleStyle = TextStyle(
fontFamily = FontFamily.Serif,
fontSize = sp,
fontWeight = FontWeight.Bold
)
tooltipBodyStyle = TextStyle(
fontSize = sp
)
taskListTextStyle = TextStyle(
fontSize = sp
)
groupHeaderTextStyle = TextStyle(
fontSize = sp
)
}
}
val customTheme = ganttTheme {
naming {
// Change text labels
taskListHeader = "Activities"
taskGroups = "Department"
noGroupsMessage = "No departments assigned"
subtasks = "Sub-activities"
}
}
val customTheme = ganttTheme {
colors {
groupColorScheme {
"Backend" to Color(0xFF2196F3)
"Frontend" to Color(0xFF4CAF50)
"DevOps" to Color(0xFFFF9800)
}
}
styles {
taskBarHeight = 0.8f
showTaskProgress = true
}
typography {
taskBarTextStyle = TextStyle(
fontFamily = FontFamily.Monospace,
fontSize = 12.sp
)
}
naming {
taskListHeader = "Sprint Tasks"
taskGroups = "Team"
}
}
GanttChartView(
state = ganttState,
ganttTheme = customTheme
)
import kotlinx.datetime.*
import kotlin.time.Duration.Companion.hours
val now = Clock.System.now().toLocalDateTime(TimeZone.UTC)
// Simple task
val task = GanttTask(
id = "task1",
name = "Design Phase",
startDate = now,
duration = 5.hours,
progress = 0.5f,
group = "Development", // Optional grouping
url = "https://example.com" // Optional clickable URL in tooltip
)
// Tasks can depend on other tasks
val task1 = GanttTask(
id = "backend",
name = "Backend Development",
startDate = now,
duration = 10.hours
)
task2 = GanttTask(
id = ,
name = ,
startDate = now.plus(hours),
duration = hours,
dependencies = listOf()
)
task3 = GanttTask(
id = ,
name = ,
startDate = now.plus(hours),
duration = hours,
dependencies = listOf(, )
)
val ganttState = remember {
GanttChartState(
tasks = listOf(task1, task2, task3, projectTask),
enableSmartOrdering = true // Automatically orders tasks by dependencies
)
}
Ganttastic is built with:
Contributions are welcome! Please feel free to submit a Pull Request.
git checkout -b feature/amazing-feature)git commit -m 'Add some amazing feature')git push origin feature/amazing-feature)Please make sure to update tests as appropriate and adhere to the existing coding style.
This project is licensed under the MIT License - see the LICENSE file for details.
You can add custom content that appears above the Gantt chart and collapses on scroll:
GanttChartView(
state = ganttState,
headerContent = {
Column(modifier = Modifier.fillMaxWidth().padding(16.dp)) {
Text(
"Project Timeline Overview",
style = MaterialTheme.typography.headlineMedium,
fontWeight = FontWeight.Bold
)
// Add group info header
GroupInfoHeader(
groupInfo = ganttState.getGroupColorMapping(),
taskCountProvider = { group ->
ganttState.displayTasks.count { it.group == group }
}
)
}
}
)
Enable smart ordering to automatically arrange tasks based on dependencies:
val ganttState = GanttChartState(
tasks = tasks,
enableSmartOrdering = true
)
GanttChartView(
state = ganttState,
taskListWidth = 250.dp,
rowHeight = 40.dp,
headerHeight = 50.dp,
showTaskList = true // Toggle task list visibility
)
Project Link: https://github.com/kotlinlabs/ganttastic
val customTheme = ganttTheme {
colors {
// Define group color palette (used for automatic assignment)
groupColorPalette = listOf(
Color(0xFF2196F3), // Blue
Color(0xFF4CAF50), // Green
Color(0xFFFF9800) // Orange
)
// Explicit group color mapping
groupColorScheme {
"Development" to Color.Blue
"Testing" to Color.Green
"Deployment" to Color.Orange
}
// Override default colors
taskBarBackground = { baseColor, isHovered ->
if (isHovered) baseColor.copy(alpha = 0.8f) else baseColor.copy(alpha = 0.6f)
}
taskBarBorder = { baseColor, isHovered ->
baseColor.copy(alpha = if (isHovered) 0.9f else 0.7f)
}
taskBarProgress = { baseColor, isHovered ->
baseColor.copy(alpha = if (isHovered) 0.9f else 0.8f)
}
dependencyArrowColor = { baseColor ->
baseColor.copy(alpha = 0.7f)
}
taskBarBorder = { baseColor, isHovered ->
baseColor.copy(alpha = if (isHovered) 0.9f else 0.7f)
}
taskBarProgress = { baseColor, isHovered ->
baseColor.copy(alpha = if (isHovered) 0.9f else 0.8f)
}
dependencyArrowColor = { baseColor ->
baseColor.copy(alpha = 0.7f)
}
}
typography {
taskBarTextStyle = TextStyle(
fontFamily = FontFamily.Monospace,
fontSize = 12.sp
)
}
naming {
taskListHeader = "Sprint Tasks"
taskGroups = "Team"
}
}
val projectTask = GanttTask.createParentTask(
id = "project",
name = "Full Project",
startDate = now,
group = "Development",
children = listOf(
// This is a nested parent task
GanttTask.createParentTask(
id = "phase1",
name = "Phase 1",
startDate = now,
group = "Development",
children = listOf(
// Child tasks of Phase 1
GanttTask(
id = "task1.1",
name = "Task 1.1",
startDate = now,
duration = 2.hours,
progress = 0.5f
),
GanttTask(
id = "task1.2",
name = "Task 1.2",
startDate = now.plus(2.hours),
duration = 3.hours,
progress = 0.3f,
dependencies = listOf("task1.1") // Task dependencies
)
),
progress = 0.4f
),
// Another nested parent task
GanttTask.createParentTask(
id = "phase2",
name = "Phase 2",
startDate = now.plus(5.hours),
group = "Testing",
children = listOf(
// Child tasks of Phase 2
GanttTask(
id = "task2.1",
name = "Task 2.1",
startDate = now.plus(5.hours),
duration = 4.hours,
progress = 0.0f,
dependencies = listOf("task1.2") // Depends on task from Phase 1
)
),
progress = 0.0f
)
),
progress = 0.2f
)
Surfaced from shared tags and platforms — no rankings paid for.