Docs

Upgrading from Hilla 1.x

To migrate a Hilla 1.x application to Hilla 2.0, you’ll have to change several things in your application.

Many of the breaking changes in Hilla 2 are introduced by the following updates of the underlying Java platform dependencies:

Jakarta EE 10 & Servlet 6

Hilla 2 is based on Servlet 6 specifications and is compatible with the Jakarta EE 10. Changing from javax. to jakarta. is required for validation and some nullability annotations in Java.

Spring Boot 3

Hilla 2 uses the latest Spring Boot 3 and Spring Framework 6 versions. This leads in turn to making breaking changes in Spring-based features compared to earlier Spring-boot 2 and Spring Framework 5 versions.

Java 17

Hilla 2 requires Java 17 or later. This is dictated by Spring framework and newer versions of application servers.

Aside from these changes in Hilla dependencies, here are the notable changes in the framework itself:

Multi-Module Endpoints Parser & Generator

Hilla 2 changes the parser and generator that are used to produce TypeScript code for endpoints. As a result, some adjustments to Java endpoints and entities code in Hilla applications might be required. The hillaEngine experimental feature flag was removed.

Reactive Endpoints

Released previously as an experimental feature behind the hillaPush feature flag, reactive endpoints are now enabled by default.

Vaadin Components 24

Hilla 2 complements Vaadin 24 release. The Vaadin components, introduced in version 24, support styling using the ::part() CSS selector. They come with some changes that affect styling the component internals.

Preparation

Before migrating any application, a few tasks must be done. They’re described in the sub-sections here.

Set Up Node.js

Installing an up-to-date version of Node.js 18 is recommended before starting with Hilla 2 migration. For an optimal and smooth application development experience, make sure to have node executables in the PATH environment variable. Read the Node.js installation instructions and follow them.

Maven Wrapper

Hilla 2 requires either a Maven wrapper script in the application, or the mvn executable in the environment for configuring the endpoints parser and generator from the settings declared pom.xml.

If your project has mvnw and mvnw.cmd Maven wrapper scripts, Hilla 2 uses it to install and run Maven. The Maven Wrapper website offers download links and instructions for installing it in a project.

For installing Maven in your system environment, see the Installing Apache Maven page.

Hilla Dependency Upgrade

Upgrade the Hilla version in the pom.xml file to the latest release like so:

<hilla.version>2.0.0</hilla.version>

Find the latest version number using the GitHub Hilla releases list.

Jakarta EE 10 Namespaces

You can use the following free tools for the package name conversion:

When being applied to a project, these tools convert Java class imports, manifests, property files, and other resources to use jakarta.* namespace when needed. Conversion instructions can be found in each tool’s README file.

The last versions of IntelliJ IDEA offers migration refactoring tools, including a Java EE to Jakarta EE package converter.

Make sure that the Jakarta specifications in your project have the proper versions. Refer to the full list of Jakarta EE 10 specifications for more information.

Below are a some examples:

<dependency>
    <groupId>jakarta.servlet</groupId>
    <artifactId>jakarta.servlet-api</artifactId>
    <version>6.0.0</version>
</dependency>
<dependency>
    <groupId>jakarta.annotation</groupId>
    <artifactId>jakarta.annotation-api</artifactId>
    <version>2.1.0</version>
</dependency>
<dependency>
    <groupId>jakarta.enterprise</groupId>
    <artifactId>jakarta.enterprise.cdi-api</artifactId>
    <version>4.0.0</version>
</dependency>
<dependency>
    <groupId>jakarta.enterprise.concurrent</groupId>
    <artifactId>jakarta.enterprise.concurrent-api</artifactId>
    <version>3.0.0</version>
</dependency>

Spring Upgrade Instructions

Spring Boot 3 and Spring Framework 6 don’t fundamentally change how applications are developed. The main changes are around Jakarta EE 10 namespaces and supported products, Java version and the dependency upgrades and deprecations.

Spring Boot 3 and Framework 6 use new versions of third-party dependencies: Hibernate 6, Hibernate Validator 8, servlet containers — Jetty 11, Tomcat 10.1 and many others.

To browse the full list of changes, see the Spring-boot 3.0 Release Notes and the What’s New in Spring Framework 6.x page.

Below is a general overview of the changes needed for Spring-based Vaadin applications:

  • Upgrade Spring to the Latest

    You need to upgrade Spring versions to the latest, including the starter parent dependency:

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.0.2</version>
    </parent>
  • Deprecation

    Deprecated VaadinWebSecurityConfigurerAdapter was removed since Spring no longer has the WebSecurityConfigurerAdapter class. Use instead the VaadinWebSecurity base class for your security configuration. Below is an example of this:

    @EnableWebSecurity
    @Configuration
    public class SecurityConfig extends VaadinWebSecurity {
    
        @Value("${my.app.auth.secret}")
        private String authSecret;
    
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            super.configure(http);
    
            // Disable creating and using sessions in Spring Security
            http.sessionManagement()
                    .sessionCreationPolicy(SessionCreationPolicy.STATELESS);
    
            // Register your login view to the view access checker mechanism
            setLoginView(http, "/login");
    
            // Enable stateless authentication
            setStatelessAuthentication(http,
                    new SecretKeySpec(Base64.getDecoder().decode(authSecret), // (1)
                            JwsAlgorithms.HS256), // (2)
                    "com.example.application" // (3)
            );
        }
    }

    In this example, AuthenticationManagerBuilder — used in Spring-boot 2.x — is replaced by UserDetailsService. And http.authorizeRequests().antMatchers() are replaced by http.authorizeHttpRequests().requestMatchers().

Java Version

Java 17 or later is required. Below is an example of how to use this version:

<properties>
    <java.version>17</java.version>
    <!-- OR: -->
    <maven.compiler.source>17</maven.compiler.source>
    <maven.compiler.target>17</maven.compiler.target>
</properties>

Maven Plugins

Several plugins are available for use with Maven. Those that are relevant to upgrading a Hilla application to Hilla 2.0 are described here.

Hilla Maven Plugin

Starting from Vaadin 24, vaadin-maven-plugin alone doesn’t support generating Hilla endpoints. Replace it with hilla-maven-plugin to enable using Hilla endpoint:

<groupId>dev.hilla</groupId>
<artifactId>hilla-maven-plugin</artifactId>
<version>${hilla.version}</version>

You can use Maven goals of the former vaadin-maven-plugin the same way from the hilla-maven-plugin.

This primarily affects hybrid applications, which have both Hilla client-side and Flow server-side views, and were created with Vaadin / Fusion 23 or earlier.

Third-Party Plugin Versions

Make sure that the versions of Maven plugins — the ones defined explicitly in your project — are compatible with Java 17. For example, nexus-staging-maven-plugin requires a minimal version 1.6.13.

SLF4J 2.0

Hilla 2, Vaadin 24 and Spring Boot 3 use SLF4J library version 2.0, which has breaking changes compared to earlier versions. See the SLF4J release notes for more information.

Changes in Hilla Endpoints

The behavior of null type annotations, such as @dev.hilla.Nonnull, has been fixed according to the Java Language Specification. In particular, this affects arrays in endpoints and entity classes:

  • @dev.hilla.Nonnull String[] is a nullable array type whose items are non-null.

  • String @dev.hilla.Nonnull [] is non-null array type whose items are nullable.

  • @dev.hilla.Nonnull String @dev.hilla.Nonnull [] is a non-null array type whose items are non-null.

This also affects nullability in generated TypeScript code.

Consider using the @NonNullApi annotation for declaring nullability on the package level.

Breaking Changes in Vaadin Components

Upgrading a Hilla application to Hilla 2 can involve several breaking changes. They’re described in the following sub-sections.

Changes in Behavior & Styling

  • Badges no longer shrink by default. This can be overridden with CSS [theme~="badge"] { flex-shrink:1; }.

  • Buttons no longer shrink by default. This can be overridden with CSS vaadin-button { flex-shrink:1; }.

  • Number Field’s default width now matches that of other text input components. You can restore the old default back with CSS vaadin-number-field { width:8em; }.

  • Time Picker no longer automatically adjusts values to fit min/max constraints.

  • The default top and bottom margins of the H1…​H6 HTML elements have been removed. This change can be reverted by applying the following CSS:

    h1,h2,h3,h4,h5,h6 { margin-top: 1.25em; }
    h1 { margin-bottom: 0.75em; }
    h2, h3, h4 { margin-bottom: 0.5em; }
    h5 { margin-bottom: 0.25em; }

Changes in API of Web Components

The following changes affect the client-side APIs of Vaadin components:

  • The label on vaadin-checkbox and vaadin-radio-button must be assigned using the label property, as the default slot has been removed.

  • vaadin-confirm-dialog.cancel and .reject properties were renamed to .cancelButtonVisible and .rejectButtonVisible.

  • vaadin-number-field property has-controls were renamed step-buttons-visible.

  • Deprecated @vaadin/vaadin- (e.g., @vaadin/vaadin-grid) npm packages have been removed. Use instead the new @vaadin/ (i.e., @vaadin/grid).

  • Deprecated **Element legacy class aliases (e.g., GridElement) have been removed. Use instead the plain component classes (i.e., Grid).

  • Deprecated misspelled vaadin-icons were removed: buss, funcion, megafone, palete, and trendind-down.

  • notifyResize and updateStyles methods were removed from various components as obsolete.

  • preventInvalidInput in text input fields were removed in favor of setAllowedCharPattern.

  • The read-only theme property was removed. Use instead the theme attribute.

Update Component Styling

Click and read if you have styled Vaadin components

The internal structure of many Vaadin components has been modified to improve accessibility and enable the new, simplified styling approach in Hilla 2. These changes may affect custom CSS applied to the components.

When upgrading from Vaadin components 23 (Hilla 1) or earlier, you can refactor all CSS to the new styling approach. Another option is to stay on the old (Shadow DOM based) styling approach, and rewrite only those selectors that affect your application. Both methods can also be used in parallel, if desired.

The instructions below are based on the old approach, which is a fast way of upgrading. The new styling approach is described in the Styling section of the Vaadin documentation.

Accordion

The summary part of Accordion panels has been refactored into the separate vaadin-accordion-heading custom element, slotted into the panel component.

[part="summary"] {...}
::slotted(vaadin-accordion-heading) {...}

Parts that were inside vaadin-accordion-panel before are now in vaadin-accordion-heading:

[part="toggle"] {...}
[part="toggle"] {...}

The summary-content part was renamed to content and is now in vaadin-accordion-heading:

[part="summary-content"] {...}
[part="content"] {...}

App Layout

The background of the drawer and navbar are now defined in the parts themselves, instead of in ::before pseudo-elements:

[part="navar"]::before {
    background: ...;
}
/* and */
[part="drawer"]::before {
    background: ...;
}
[part="navbar"] {
    background: ...;
}
/* and */
[part="drawer"] {
    background: ...;
}

The navbar now has a defined min-height. This change can be reversed with the following:

[part="navbar"] {
    min-height: 0;
}

The drawer now renders a shadow when in overlay mode. It can be disabled with this:

:host([overlay]) [part="drawer"] {
    box-shadow: none;
}

Avatar Group

Individual Avatars in the Avatar Group have been moved from shadow DOM to a slot:

[part="avatar"] {...}
::slotted(vaadin-avatar) {...}

Context Menu

The context-menu item element, vaadin-context-menu-item, no longer extends the vaadin-item element, and therefore no longer inherits styling from it. Target vaadin-context-menu-item instead of vaadin-item for separate styling of the context-menu items.

CRUD

The "new item" button has been moved from the CRUD’s shadow DOM to a slot, and the new-button attribute has been removed from it:

[new-button] {...}
/* or */
vaadin-button {...}
::slotted([slot="new-button"])

The Grid inside the CRUD web component has been moved out of the CRUD’s shadow DOM to a slotted element.

Date Picker

The buttons in the Date Picker’s overlay have been moved from shadow DOM to slots:

[part="today-button"] {...}
/* and */
[part="cancel-button"] {...}
::slotted([slot="today-button"]) {...}
/* and */
::slotted([slot="cancel-button"]) {...}
/* or target both with */
::slotted(vaadin-button) {...}

The date cells in the calendar can have multiple part names to reflect their states, so the part attribute selector must use the ~= operator to match individual words:

[part="date"] {...}
[part~="date"] {...}

The state attributes for date cells have been replaced with part names:

[part="date"][disabled] {...}
[part="date"][focused] {...}
[part="date"][selected] {...}
[part="date"][today] {...}
[part~="date"][part~="disabled"] {...}
[part~="date"][part~="focused"] {...}
[part~="date"][part~="selected"] {...}
[part~="date"][part~="today"] {...}

Details

The summary part has been refactored into a separate custom element, slotted into the Details component:

[part="summary"] {...}
::slotted(vaadin-details-summary) {...}

The toggle part is now in the new vaadin-details-summary element:

[part="toggle"] {...}
[part="toggle"] {...}

The summary-content part is now in the vaadin-details-summary element, and renamed content:

[part="summary-content"] {...}
[part="content"] {...}

Login

The "Forgot password" button has been moved from shadow DOM to a slot:

#forgotPasswordButton {...}
/* or */
vaadin-button[theme~="forgot-password"] {...}
/* or */
vaadin-button {...}
::slotted([slot="forgot-password"]) {...}

The menu-bar buttons, which are also the top-level menu items, have been moved from shadow DOM to a slot:

[part="menu-bar-button"] {...}
/* or */
vaadin-menu-bar-button {...}
::slotted(vaadin-menu-bar-button) {...}

The items in the Menu Bar drop-down menus are now vaadin-menu-bar-item instead of vaadin-context-menu-item, and therefore do not inherit styling from Context Menu items.

Message Input

The text area and button have been moved from shadow DOM to slots, and replaced with regular Text Area and Button instances:

vaadin-message-input-text-area {...}
/* and */
vaadin-message-input-button {...}
::slotted(vaadin-text-area) {...}
/* and */
::slotted(vaadin-button) {...}

Message List

The message elements in the list have been moved from shadow DOM to a slot:

vaadin-message {...}
::slotted(vaadin-message) {...}

Avatars in messages have been moved to their own slots, and replaced with regular vaadin-avatar instances:

[part="avatar"] {...}
/* or */
vaadin-message-avatar {...}
::slotted(vaadin-avatar) {...}

Multi-Select Combo Box

The chip elements, as well as the overflow chip, have been moved from shadow DOM to a slot:

vaadin-multi-select-combo-box-chip {...}
[part~="chip"] {...}
[part~="overflow"] {...}
[part~="overflow"][part~="overflow-one"] {...}
::slotted(vaadin-multi-select-combo-box-chip) {...}
::slotted([slot="chip"]) {...}
::slotted([slot="overflow"]) {...}
::slotted([slot="overflow"][count="1"]) {...}

Upload

The file list has been refactored into its own vaadin-upload-file-list custom element, slotted into the Upload component:

[part="file-list"] {...}
::slotted(vaadin-upload-file-list) {...}

The upload button has been moved from shadow DOM to a slot:

[part="upload-button"] {...}
/* or*/
#uploadButton {...}
/* or */
vaadin-button {...}
::slotted(vaadin-button) {...}

The drop label and icon have been moved from shadow DOM to slots, and the icon is now a vaadin-upload-icon element:

#dropLabel {...}
/* and */
[part="drop-label-icon"] {...}
::slotted([slot="drop-label"]) {...}
/* and */
::slotted(vaadin-upload-icon) {...}