Quick-Start Guide

OptoGantt — Vaadin

Follow these steps to add an interactive Gantt chart to your Vaadin application. By the end you will have the chart shown below running in your own project, ready to connect to your own data.

Live example — what you will build

This chart is fully interactive — use the filter pane to adjust the date range and period. The stripe on the bar crossing today's date is the built-in "now" indicator.

Part 1 — Display-only Gantt chart

Start here. Steps 1–5 give you a working Gantt chart with no user interaction. If you only need to display data, you can stop after Step 5.

1
Install the JAR files

Download optogantt-1.0.0.zip from Gumroad and unzip it. Install both JARs to your local Maven repository by running the following command once for each, substituting the filename:

Shell
mvn install:install-file \
  -Dfile=gantt-core-1.0.0.jar \
  -DgroupId=com.optomus.gantt \
  -DartifactId=gantt-core \
  -Dversion=1.0.0 \
  -Dpackaging=jar

Repeat for gantt-vaadin-1.0.0.jar (artifactId: gantt-vaadin). The remaining JARs are for other frameworks and are not needed for this guide.

2
Add Maven dependencies

Add the Vaadin BOM to dependencyManagement and declare vaadin-spring-boot-starter and gantt-vaadin as dependencies:

pom.xml
<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>com.vaadin</groupId>
            <artifactId>vaadin-bom</artifactId>
            <version>24.3.11</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

<dependencies>
    <dependency>
        <groupId>com.vaadin</groupId>
        <artifactId>vaadin-spring-boot-starter</artifactId>
    </dependency>
    <dependency>
        <groupId>com.optomus.gantt</groupId>
        <artifactId>gantt-vaadin</artifactId>
        <version>1.0.0</version>
    </dependency>
</dependencies>

Also add the vaadin-maven-plugin to your build so Vaadin's frontend toolchain bundles the CSS from the gantt-vaadin JAR:

pom.xml — build/plugins
<plugin>
    <groupId>com.vaadin</groupId>
    <artifactId>vaadin-maven-plugin</artifactId>
    <version>24.3.11</version>
    <executions>
        <execution>
            <goals>
                <goal>prepare-frontend</goal>
                <goal>build-frontend</goal>
            </goals>
        </execution>
    </executions>
</plugin>
Note: gantt-core is a transitive dependency of gantt-vaadin and is resolved automatically. You do not need to declare it separately.
3
Build your Gantt bars

Create your bar data using GanttBar. Each bar requires a task name, row name, start datetime, end datetime, text colour, bar background colour, hover text, and an optional hyperlink (null if not needed).

YourView.java
import com.optomus.gantt.core.model.GanttBar;
import com.optomus.gantt.core.model.GanttColour;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.List;

LocalDate today = LocalDate.now();

List<GanttBar> bars = List.of(
    new GanttBar(
        "Project Kickoff",              // task name
        "Alice",                        // row name
        LocalDateTime.of(today.minusDays(5), LocalTime.of(9,  0)),
        LocalDateTime.of(today.minusDays(1), LocalTime.of(17, 0)),
        GanttColour.WHITE,              // text colour
        new GanttColour("#2a4a7a"),     // bar colour
        "Project Kickoff | Alice | Completed",  // hover text
        null                            // hyperlink
    ),
    new GanttBar(
        "Development Sprint",
        "Bob",
        LocalDateTime.of(today.minusDays(3), LocalTime.of(9,  0)),
        LocalDateTime.of(today.plusDays(4),  LocalTime.of(17, 0)),
        GanttColour.WHITE,
        new GanttColour("#1a6b5a"),
        "Development Sprint | Bob | In progress",
        null
    ),
    new GanttBar(
        "Testing & QA",
        "Carol",
        LocalDateTime.of(today.plusDays(2),  LocalTime.of(9,  0)),
        LocalDateTime.of(today.plusDays(6),  LocalTime.of(17, 0)),
        GanttColour.WHITE,
        new GanttColour("#5b3a6e"),
        "Testing & QA | Carol | Upcoming",
        null
    )
);
Row order: rows appear in the chart in the order they are first encountered in the bar list, unless you specify row names explicitly via explicitRowNames() on the Builder.
4
Build the chart using the Builder

GanttChart uses a Builder pattern. Construct it directly in your Vaadin view's constructor — no Spring injection or build() call is needed.

YourView.java
import com.optomus.gantt.vaadin.components.GanttChart;
import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;

LocalDateTime chartStart = LocalDateTime.of(
    LocalDate.now().minusDays(6), LocalTime.MIDNIGHT);

GanttChart ganttChart = new GanttChart.Builder()
    .startDate(chartStart)
    .columnDuration(Duration.ofDays(1))  // one column per day
    .numColumns(14)                       // 14-day window
    .ganttBars(bars)
    .explicitRowNames(List.of("Alice", "Bob", "Carol"))
    .build();
No separate build() call: unlike the Spring edition, the Builder constructs and fully initialises the chart in one step.
5
Add the chart to your Vaadin view

GanttChart extends Vaadin's Div. Add it to your view with a single add() call — no Thymeleaf fragments, no CSS link tags, no template files.

YourView.java
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.router.Route;

@Route("schedule")
public class ScheduleView extends VerticalLayout {

    public ScheduleView() {
        List<GanttBar> bars = buildBars();

        GanttChart ganttChart = new GanttChart.Builder()
            .startDate(LocalDateTime.of(
                LocalDate.now().minusDays(6), LocalTime.MIDNIGHT))
            .columnDuration(Duration.ofDays(1))
            .numColumns(14)
            .ganttBars(bars)
            .explicitRowNames(List.of("Alice", "Bob", "Carol"))
            .build();

        add(ganttChart);
    }
}
CSS is automatic: gantt-vaadin uses Vaadin's @CssImport mechanism. The stylesheet is bundled by the Vaadin frontend toolchain at build time — no manual CSS wiring is required.
If you only need a display-only chart, stop here. Steps 1–5 are all you need. Continue below to add the interactive filter pane.

Part 2 — Interactive filter pane

Add the filter pane so visitors can adjust the chart's date range and period. Unlike the Spring edition, there are no REST endpoints, no JSON, and no JavaScript to write — Vaadin handles all client-server communication automatically via WebSocket.

6
Construct GanttFilterPane and wire it to the chart

Construct GanttFilterPane with a GanttFilterListener implementation. The listener receives fully resolved chart parameters — start date, column duration, and column count — whenever the visitor applies or clears the filter. Call syncWithView() to initialise the pane's fields with the current chart state.

YourView.java
import com.optomus.gantt.core.GanttFilterListener;
import com.optomus.gantt.core.model.GanttColumnDuration;
import com.optomus.gantt.vaadin.components.GanttChart;
import com.optomus.gantt.vaadin.components.GanttFilterPane;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.router.Route;
import java.time.*;
import java.util.List;

@Route("schedule")
public class ScheduleView extends VerticalLayout {

    private GanttChart    ganttChart;
    private GanttFilterPane filterPane;
    private LocalDateTime chartStart     = defaultStart();
    private Duration      columnDuration = Duration.ofDays(1);
    private int           numColumns     = 14;

    public ScheduleView() {
        List<GanttBar> bars = buildBars();
        ganttChart  = buildChart(bars);
        filterPane  = new GanttFilterPane(new GanttFilterListener() {
            @Override
            public void onFilter(LocalDateTime start,
                                 Duration duration, Integer cols) {
                chartStart     = start;
                columnDuration = duration;
                numColumns     = cols;
                replaceChart(buildChart(bars));
                filterPane.syncWithView(chartStart,
                    GanttColumnDuration.FOURTEEN_DAYS);
            }
            @Override
            public void onClearFilter() {
                chartStart     = defaultStart();
                columnDuration = Duration.ofDays(1);
                numColumns     = 14;
                replaceChart(buildChart(bars));
                filterPane.syncWithView(chartStart,
                    GanttColumnDuration.FOURTEEN_DAYS);
            }
        });

        filterPane.syncWithView(chartStart, GanttColumnDuration.FOURTEEN_DAYS);
        add(filterPane, ganttChart);
    }

    private GanttChart buildChart(List<GanttBar> bars) {
        return new GanttChart.Builder()
            .startDate(chartStart)
            .columnDuration(columnDuration)
            .numColumns(numColumns)
            .ganttBars(bars)
            .explicitRowNames(List.of("Alice", "Bob", "Carol"))
            .build();
    }

    private void replaceChart(GanttChart newChart) {
        if (ganttChart != null) replace(ganttChart, newChart);
        ganttChart = newChart;
    }

    private static LocalDateTime defaultStart() {
        return LocalDateTime.of(
            LocalDate.now().minusDays(6), LocalTime.MIDNIGHT);
    }
}
No endpoints, no JavaScript: the filter pane communicates with the server entirely through Vaadin's WebSocket connection. GanttFilterListener fires server-side — update your chart state and call replaceChart() to push the new chart to the browser automatically.
7
Enable server push with AppShellConfigurator

Vaadin requires @Push to be declared on a class implementing AppShellConfigurator rather than on individual views. Create this class once in your application — it enables WebSocket server push globally for all Vaadin views.

AppShell.java
import com.vaadin.flow.component.page.AppShellConfigurator;
import com.vaadin.flow.component.page.Push;

@Push
public class AppShell implements AppShellConfigurator {
}
Why AppShellConfigurator? Vaadin validates push configuration at startup. Placing @Push directly on a view class causes an InvalidApplicationConfigurationException at startup. The AppShellConfigurator class is the single correct location for application-level Vaadin configuration.
Your chart is now fully interactive. Apply Filter and Clear filter both update the chart instantly via WebSocket — no page reload, no JavaScript to write. Continue below for optional customisation.

Part 3 — Optional customisation

These steps are independent of each other — apply any combination that suits your use case.

8
Customise chart colours Optional

Override the default heading, border, and alternate row colours via the Builder. All colours accept CSS hex strings via GanttColour.

YourView.java
new GanttChart.Builder()
    .startDate(chartStart)
    .columnDuration(columnDuration)
    .numColumns(numColumns)
    .ganttBars(bars)
    .headingTextColour(new GanttColour("#ffffff"))
    .headingBackgroundColour(new GanttColour("#3d2b1f"))
    .borderColour(new GanttColour("#6b4c35"))
    .alternateRowsBackgroundColour(new GanttColour("#faf7f3"))
    .build();

If only headingBackgroundColour() is set, border and alternate row colours are derived automatically as darker and lighter shades respectively.

9
Customise row height and name column width Optional
YourView.java
new GanttChart.Builder()
    // ... required parameters ...
    .nameColumnWidth("180px")  // any valid CSS width
    .rowHeightInPixels(40)
    .build();
10
Internationalise labels via a properties file Optional

All filter pane labels are loaded from GanttMessages.properties inside the gantt-core JAR via Java's ResourceBundle. To override any label, place your own GanttMessages.properties earlier on the classpath — for example in src/main/resources/com/optomus/gantt/core/.

GanttMessages.properties — overridable keys
# Filter pane labels
label.startDate=Start
label.columnDuration=Period
button.apply=Apply Filter
link.clear=Clear filter

# Period dropdown options
duration.FOURTEEN_DAYS=14 Days
duration.ONE_MONTH=1 Month
duration.THREE_MONTHS=3 Months
duration.SIX_MONTHS=6 Months
duration.ONE_YEAR=1 Year
No Spring MessageSource needed: unlike the Spring edition, label resolution uses Java's standard ResourceBundle directly. Locale-specific files such as GanttMessages_fr.properties are picked up automatically based on the JVM default locale.
11
Add hyperlinks to bars Optional

Pass a URL as the final constructor argument to GanttBar. Clicking the bar navigates to that URL. Pass null if no link is needed.

YourView.java
new GanttBar(
    "Sprint Review",
    "Alice",
    start, end,
    GanttColour.WHITE,
    new GanttColour("#2a4a7a"),
    "Sprint Review | Alice | Click for details",
    "https://your-project-tracker.example.com/sprint/42"
)

Troubleshooting

Common issues and their solutions.

!
InvalidApplicationConfigurationException at startup

@Push has been placed on a view class rather than on an AppShellConfigurator class. Create an AppShell class as shown in Step 7 and remove @Push from your view.

!
Chart renders but has no styling

The vaadin-maven-plugin's prepare-frontend and build-frontend goals must run during the build. Confirm the plugin is declared in pom.xml and that you are building with mvn package or mvn install rather than just mvn compile. On first run, Vaadin downloads its frontend toolchain — allow a few extra minutes.

!
Apply Filter button does nothing

Confirm that AppShell implementing AppShellConfigurator with @Push exists on the classpath (Step 7). Without server push, UI updates triggered by GanttFilterListener do not propagate to the browser. Also confirm that replaceChart() is called inside onFilter().

!
Route not found

Vaadin scans for @Route classes in the same package as your @SpringBootApplication class and its sub-packages. If your view is in a different package, add it to scanBasePackages:

YourApplication.java
@SpringBootApplication(scanBasePackages = {
    "com.example.yourapp",
    "com.example.yourapp.views"
})
public class YourApplication { ... }
!
Vaadin intercepts existing Spring MVC routes

By default Vaadin's servlet maps to /* and intercepts all requests. If your application mixes Vaadin views with Spring MVC controllers, restrict Vaadin to its own path prefix in application.properties:

application.properties
vaadin.url-mapping=/vaadin/*

Vaadin routes are then accessible under /vaadin/schedule rather than /schedule.

Next steps

Try the live demo

Visit gantt.nz to explore the full interactive demo — add your own bars, adjust the filter pane, and see all features in action.

Get support

Questions or unexpected behaviour? Email support@gantt.nz. We aim to respond within 24 hours on New Zealand business days.