Documentation versions (currently viewingVaadin 24)

Web Applications with Spring Boot & Vaadin

Tutorial on building, testing, and deploying a modern web application using Spring Boot and Vaadin.

This is a practical introduction to web application development with Spring Boot and Vaadin. Spanning several parts, it covers the entire development process, from setup to deployment, following a step-by-step approach. To get the most out of this tutorial, replicate each section as you follow along.

The content is suitable for anyone familiar with Java who wants to build a web application. For a more robust and productive learning experience, you’ll start with setting up a development environment.

This guide teaches you how to build a functional, full-stack web application using modern Java. It focuses on real-world developer needs, without too much detail. However, links to relevant extra reading are provided when you want or need them.

The example application used is a Customer Relationship Management (CRM) system for managing contacts. It has a few features:

  • Log-in screen to restrict access.

  • Responsive layout with side navigation that works on desktop and mobile.

  • Database for persistent data storage.

  • List view that can be sorted and filtered.

  • Form to edit and add contacts.

  • Dashboard view.

  • Cloud deployment.

  • Application installation on mobile and desktop.

You can see how this application looks in the screenshot here:

A web application with a listing of contacts and an editor open.

Requirements & Source Code

This multi-part tutorial typically requires about four hours to complete. It’s intended for developers with a basic understanding of Java. You don’t need to be an expert, but understanding the syntax and basic concepts of Java makes it easier to follow along.

If you need help or clarification, though, the best place to ask questions is on the Vaadin Discord chat server.

Vaadin Flow runs on Java. To use it, you’ll need the following development tools:

You can find the full source code for this guide on GitHub.

Tools & Frameworks

This tutorial uses Vaadin and Spring Boot. They’re both easy to learn and suitable for production use.

On the backend, the application uses Spring Boot. It eliminates most of the hassle of setting up and running a Spring-based application and lets you focus on your own code.

It has many features. The main ones you’ll use are:

Don’t worry if you don’t know these terms yet. The tutorial parts that follow cover each of them.

On the frontend, the application uses Vaadin Flow. Vaadin Flow is an open-source Java web application framework that comes with a few key elements:

  • A large library of UI components. Each component has a Java API that you can customize for a look-and-feel you prefer.

  • A router for navigating between views.

  • A powerful data-binding system for forms and lists.

Vaadin Comparisons

Before putting all of this time into learning about how to use Vaadin, if you’re familiar with the alternatives, it might be useful for you to know how Vaadin is better, to know its advantages.

Spring MVC and Thymeleaf, JSP or FreeMarker

Vaadin is an alternative to using Spring MVC and Thymeleaf, JSP, or FreeMarker when building web applications with Spring Boot. Vaadin has many advantages over these alternatives. Here’s a list of the main advantages:

  • Vaadin is designed for building interactive Single-Page Applications (SPAs). Spring MVC and templates are better suited for more static content.

  • Vaadin offers a Java component API, allowing you to build the entire application in Java.

  • Vaadin comes with a large library of customizable UI components.

  • Vaadin handles the communication between the server and the browser automatically. You don’t need to write any JavaScript to update content dynamically.

REST with React or Angular

Combining a Spring Boot-based REST backend with a frontend framework like React or Angular is a popular way of building SPAs. However, Vaadin allows you to build similar application experiences with less effort. The advantages of using Vaadin over these other methods are several:

  • Faster development: you don’t need to develop the backend and frontend, separately.

  • You can write the entire application in type-safe Java.

  • Vaadin comes with a large library of customizable UI components.

  • Vaadin handles the communications between the server and the browser, automatically. You don’t need to write any JavaScript to update content, dynamically.

  • It’s more secure. The Vaadin application runs on the server and doesn’t expose application code or extra endpoints to the browser.

You can compare Vaadin, Angular, React, and Vue on the Framework Comparison page.

migration assistance

Download free e-book.
The complete guide is also available in an easy-to-follow PDF format.

Open in a
new tab
export class RenderBanner extends HTMLElement {
  connectedCallback() {

  renderBanner() {
    let bannerWrapper = document.getElementById('tocBanner');

    if (bannerWrapper) {

    let tocEl = document.getElementById('toc');

    // Add an empty ToC div in case page doesn't have one.
    if (!tocEl) {
      const pageTitle = document.querySelector(
        'main > article > header[class^=PageHeader-module--pageHeader]'
      tocEl = document.createElement('div');

      pageTitle?.insertAdjacentElement('afterend', tocEl);

    // Prepare banner container
    bannerWrapper = document.createElement('div'); = 'tocBanner';

    // Banner elements
    const text = document.querySelector('.toc-banner-source-text')?.innerHTML;
    const link = document.querySelector('.toc-banner-source-link')?.textContent;

    const bannerHtml = `<div class='toc-banner'>
          <a href='${link}'>
            <div class="toc-banner--img"></div>
            <div class='toc-banner--content'>${text}</div>

    bannerWrapper.innerHTML = bannerHtml;

    // Add banner image
    const imgSource = document.querySelector('.toc-banner-source .image');
    const imgTarget = bannerWrapper.querySelector('.toc-banner--img');

    if (imgSource && imgTarget) {