Docs

Documentation versions (currently viewingVaadin 25 (prerelease))

Upgrading from Vaadin 24

Changes needed to upgrade an application from Vaadin 24 to the latest version.

This guide goes through the changes you’ll need to make in your applications when upgrading from Vaadin 24 to the latest version. After making them, your application should compile, run, behave, and look the way it did before you upgraded.

Tip
Upgrading from Earlier Version
See Vaadin 23 to 24 Upgrade Instructions if you’re upgrading from a version earlier than Vaadin 24.

Many of the breaking changes are needed because of fundamental changes in the Java platform and the major dependencies on which Vaadin relies. This includes the following:

Java 21

Vaadin 25 requires Java 21 or later. Java 21 is the Long Term Support (LTS) version of Java. Upgrading to Java 21 might require you to upgrade other dependencies in your application.

Spring Boot 4

Vaadin 25 uses the latest Spring Boot 4 and Spring Framework 7 versions. This leads to making breaking changes in Spring-based features, compared to earlier Spring Boot 3.5 and Spring Framework 6 versions. Details can be found in Spring Boot 4 Migration Guide.

Servlet 6.1

Vaadin 25 is based on Servlet 6.1 specification, which is compatible with Jakarta EE 11. When upgrading from Vaadin 24 (Servlet 6 and Jakarta EE10), changes are typically not needed.

Gradle 8/9

Gradle 8 (8.14 and later) and Gradle 9 releases are supported.

Jackson 3

Elemental has been replaced with Jackson while Jackson version has been updated to 3. This only affects you if your application uses some of the affected low-level APIs. Details can be found in Finalize Jackson conversion and its sub-issues.

Node.js 24

Vaadin 25 requires Node.js 24 or later for building the frontend part of the application. Node.js 24 becomes the active Long Term Support (LTS) before Vaadin 25.0.0 - guaranteeing the longest possible support.

React 19

Vaadin 25 uses React 19 for the React-based components and views. Details about the changes in React 19 can be found in React 19 Upgrade Guide.

Overview

Vaadin 25 doesn’t change fundamentally how applications are developed and behave. Nevertheless, the upgrade process requires the following essential tasks and tests:

Preparation

Upgrade the Vaadin version in the project’s pom.xml file, checking for the latest Vaadin 25 release in GitHub.

Upgrade Java

Upgrade your application to use Java 21 or later.

Upgrade Spring

For Spring-based applications, upgrade to Spring Boot 4 or Spring Framework 7, depending on which is used in your project. For non-Spring applications, upgrade the application server version to one that’s compatible with Jakarta EE 11.

Other Dependencies

Upgrade third-party dependencies used in your project (e.g., Maven/Gradle plugins, libraries, frameworks) to compatible versions.

Verify & Test

Ensure your application is not using deprecated code fragments.

Make sure your application runs well on Java 21 runtime.

Preparation

Upgrade the Vaadin version in the pom.xml and gradle.properties files to the latest release like so:

Source code
XML
<vaadin.version>24.8.3</vaadin.version>
XML
properties
properties

See the list of releases on GitHub for the latest one.

Java Version

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

Source code
XML
<properties>
    <java.version>21</java.version>
    <!-- OR: -->
    <maven.compiler.source>21/maven.compiler.source>
    <maven.compiler.target>21</maven.compiler.target>
</properties>
XML
kotlin
kotlin
groovy
groovy

Spring Upgrade Instructions

To browse a full list of changes, see the Spring Boot 4.0 Release Notes and the What’s New in Spring Framework 7.x page.

The following sections provide a general overview of the changes needed for Spring-based Vaadin applications.

Upgrade Spring to Latest

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

Source code
pom.xml
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>4.0.0</version>
</parent>

Application Servers

Before migrating, find the corresponding version of the Jakarta EE 11-compatible application server used in your project. See Jakarta Compatible Products for more information.

Maven & Gradle Plugins

Ensure that the Maven plugins which are explicitly defined in your project, are compatible with Java 21. A safe choice: Maven 3.9.11 (or the latest in the 3.9 line) or Maven 4.0.x (once stable) if you are comfortable with that.

To run Gradle on top of Java 21 and latest Spring Boot 4 versions, you’ll need to use version 8.14 or later. See the Gradle release notes for further details. If your project uses Spring Boot, upgrade the plugin org.springframework.boot to version 4.0.0.

If you’re using a Gradle wrapper, update it to version 8.14 by executing the following from the command line:

Source code
terminal
./gradlew wrapper --gradle-version 8.14

For Java 21 compatibility, you may need to update the sourceCompatibility setting in your project’s build file to version 21. Check your project’s build file and make any necessary changes.

Quarkus

Vaadin Quarkus extension is changed to build production package by default. No need for production profile with exclusions for development tools in Maven configurations because Vaadin Quarkus extension has build-in Vaadin plugin handling production packaging.

To allow project to keep build configuration unchanged, Vaadin Quarkus extension has vaadin.build.enabled property to change the default behavior. Disable Vaadin plugin by adding vaadin.build.enabled=false in application.properties file to keep using profile based configuration.

Themes and Styling

Vaadin 25 simplifies the theme/styling system to bring it closer to normal/native web development, and minimizes Vaadin-specific peculiarities, while keeping migration from earlier versions as painless as possible.

Below are the main highlights of the changes and more detailed instructions are described in Theming System Renewal.

The special frontend/themes folder, and the components sub-folder for CSS shadow-DOM injection, is deprecated (but still supported).

Injecting CSS into Vaadin components’ shadow DOM through the components sub-folder in your theme folder is disabled by default. Shadow DOM styling is no longer recommended (as of V24), but if you still need to use it, it can be enabled with the feature flag themeComponentStyles.

The @Theme annotation is deprecated. Instead, StyleSheet annotation to be used for loading one or more stylesheets from public static resources locations (e.g. META-INF/resources/), whereas CssImport loads one or more stylesheets from the src/main/frontend/ folder and use mechanisms native to HTML, CSS, and React (e.g. @import url("morestyles.css") in CSS).

StyleSheet annotation is now a recommended way to load Vaadin theme for the application — to be placed on the application class implementing AppShellConfigurator. Below are some examples of how to use it:

Source code
Java
// theme selection
@StyleSheet(Aura.STYLESHEET) // or Lumo.STYLESHEET
public class Application implements AppShellConfigurator {}

// or loading custom styles and theme:

@StyleSheet("styles.css")
public class Application implements AppShellConfigurator {}

// then using @import in the src/main/resources/META-INF/resources/styles.css:

@import 'aura/aura.css'; /* or 'lumo/lumo.css' */
// your custom styles go here ...

The theme.json configuration file is deprecated (but still supported, except the lumoImports property).

The themeFor parameter of the @CssImport annotation (for shadow-DOM injection) is deprecated (but still supported).

The special document.css file (for loading styles into the document root in embedded components) is removed as no longer necessary.

Lumo Theme

The Lumo theme is no longer loaded by default, except if you’re using the @Theme annotation to load an application theme folder. If you’re not using @Theme, then add a @StyleSheet annotation to either your application class or a root layout to load the Lumo theme:

Source code
Java
@StyleSheet(Lumo.STYLESHEET);
public class Application implements AppShellConfigurator {}

All Lumo styles, including badges, but excluding Lumo Utility Classes are included by default when the Lumo theme is loaded. To load the utility classes, add a separate @StyleSheet annotation:

Source code
Java
@StyleSheet(Lumo.STYLESHEET);
@StyleSheet(Lumo.UTILITY_STYLESHEET);
public class Application implements AppShellConfigurator {}
Note
The way the Lumo theme is injected into Vaadin components has been refactored to not use the registerStyles() helper. This should not cause any breaking changes in applications; please report issues at vaadin/web-components if you find otherwise.

Material Theme

The Material theme is no longer supported in Vaadin 25. You can migrate your application to the Lumo or Aura theme or implement your own Material Design theme on top of the new component base styles.

Component Base Styles

The un-themed base styles in Vaadin components have changed significantly in Vaadin 25. They are now much less bare-bones and actually provide a better starting point for custom themes. This does mean that custom themes built on top of the Vaadin 25 component base styles need to be heavily refactored. The components’ Styling pages provide lists of style properties (CSS custom properties) that make them easier to customize.

WebComponentExporter

The WebComponentExporter feature in Flow allows you to export Flow components as Web Components for embedding into non-Vaadin user interfaces. In Vaadin 25, stylesheets loaded into exported components using the @CssImport annotation only load those styles into the exported component’s shadow DOM, not the surrounding page as before. To load the same styles into the surrounding page, import the stylesheet to it separately.

React Components

The Lumo CSS files have been removed from the @vaadin/react-components package. As mentioned above, the Lumo theme should be imported from @vaadin/vaadin-lumo-styles instead.

Source code
TypeScript
/* If imported through a CSS file */
@import '@vaadin/react-components/css/Lumo.css';

/* If imported through Typescript */
import '@vaadin/react-components/css/Lumo.css';
Source code
TypeScript
/* If imported through a CSS file */
@import '@vaadin/vaadin-lumo-styles/lumo.css';

/* If imported through Typescript */
import '@vaadin/vaadin-lumo-styles/lumo.css';

One exception is the @vaadin/react-components/css/lumo/Utility.module.css CSS module, which has been preserved for backward compatibility as the Lumo package does not expose utilities as a CSS module.

Optional Changes

These changes are optional, as old approaches still work (with the exceptions listed in the Breaking Changes section), but recommended to get your application to the new best practices in Vaadin 25, and to avoid breaking changes in later major versions.

  • Refactor component styles from shadow DOM styles to normal CSS (this was the recommended approach already in V24)

  • Load custom styles through a master stylesheet with @StyleSheet instead of @Theme or multiple @CssImport-s

  • Load additional custom stylesheets through master stylesheet with @import

  • Move stylesheets from frontend/themes/<mytheme> to src/main/resources/META-INF/resources

Components

App Layout

The bottom attribute was removed and can no longer be used to target the bottom navbar. Instead, use the selector ::part(navbar-bottom) to target it with CSS.

The Cookie Consent component has been removed. Vaadin does not provide any replacement, but several third party options exist, such as orestbida/cookieconsent.

Confirm Dialog

The Flow ConfirmDialog now only implements HasComponents instead of HasOrderedComponents. The following methods are not available anymore: replace, indexOf, getComponentCount, getComponentAt, getChildren.

Methods that allowed passing an Element instance have been removed. Use the corresponding alternatives that allow passing a Component instance instead.

Context Menu

The add method has been removed from the Flow ContextMenu. Instead, use addItem to add menu items, or addComponent to add generic components without wrapping them into a menu item.

CRUD

The “New Item” button in the CRUD component no longer uses the primary style variant by default. To get the old default back:

Source code
Java
crud.getNewButton().addThemeVariants(ButtonVariant.LUMO_PRIMARY);

Charts

The setWidthAdjust / getWidthAdjust methods of the Title class have been removed because it was removed from the underlying Highcharts library.

The DrillUpButton class has been removed from the codebase and all of its related API, e.g., setDrillUpButton / getDrillUpButton from the Drilldown class. Use Breadcrumbs instead. Likewise, the setDrillUpText / getDrillUpText has been removed from the Lang class.

All methods that accept Date as parameter that were previously marked as deprecated have been removed.

Chart configurations are now serialized using Jackson 3. The ChartSerialization.setObjectMapperInstance method that can be used to customize serialization behavior now expects a tools.jackson.databind.ObjectWriter instance.

Date Picker and Date Time Picker

The following changes have been made to the internal DOM structure of the Date Picker overlay, which may affect custom styling:

  • The vaadin-date-picker-overlay-content element is now a CSS grid layout instead of a flexbox.

  • The overlay-header part has been removed.

Date Time Picker

In the Flow DateTimePicker component, validation is no longer triggered on blur if the value has remained unchanged after user interaction, making this behavior consistent with the rest of the field components, which already received a similar update in V24.

Incomplete input, where only a date or only a time is entered, is now treated as invalid. The corresponding error message can be configured via DateTimePickerI18n:

Source code
Java
dateTimePicker.setI18n(new DateTimePickerI18n()
    .setIncompleteInputErrorMessage("Please enter both date and time"));

Details

The setContent and addContent methods have been removed from the Flow Details component. Use regular methods from HasComponents such as add, remove, removeAll instead.

Dialog and Confirm Dialog

Dialog and ConfirmDialog do not show a closing animation anymore when removing the component from the UI / DOM. Instead, the dialog should be closed and the closed event needs to be used to wait for the closing animation to finish before removing the component.

For Flow this is relevant when manually adding / removing the dialog from the UI. The event is not needed when calling dialog.open() without adding the dialog to the UI.

Source code
Java
var dialog = new Dialog();
add(dialog);
dialog.open();

// When dialog is not needed anymore
remove(dialog);
Source code
Java
var dialog = new Dialog();
dialog.addClosedListener(e -> remove(dialog));
add(dialog);
dialog.open();

// When dialog is not needed anymore
dialog.close();

For Hilla / React this is relevant when rendering dialogs conditionally.

Source code
TypeScript
const opened = useSignal(true);

{ opened ? <Dialog opened={true}/> : null }

// When dialog is not needed anymore
opened.value = false;
Source code
TypeScript
const ref = useRef<DialogElement>(null);
const opened = useSignal(true);

{
  opened
  ? <Dialog opened={true} ref={ref} onClosed={() => opened.value = false}/>
  : null
}

// When dialog is not needed anymore
ref.current?.close();

Form Layout

The following custom CSS properties have been removed from vaadin-form-item:

  • --vaadin-form-item-label-width

  • --vaadin-form-item-label-spacing

  • --vaadin-form-item-row-spacing

Use the following CSS properties on vaadin-form-layout instead:

  • --vaadin-form-layout-label-width

  • --vaadin-form-layout-label-spacing

  • --vaadin-form-layout-row-spacing

Map

The Map component’s borderless / BORDERLESS style variant has been renamed no-border / NO_BORDER for consistency with other components.

The TestBench API MenuBarElement.OVERLAY_TAG has been removed. To get a reference to a sub-menu, instead use MenuBarButtonElement.openSubMenu which returns a reference.

Message Input

The send button no longer uses the Primary style variant by default. To revert this change you can style the button with CSS:

Source code
CSS
vaadin-message-input > vaadin-message-input-button {
  background-color: var(--lumo-primary-color);
  color: var(--lumo-primary-contrast-color);
}

Also, the send button now is a vaadin-message-input-button instead of vaadin-button.

Multi-Select Combo Box

The Multi-Select Combo Box no longer uses vaadin-multi-select-combo-box-internal internally. This may affect custom shadow DOM styling of the component.

Overlays

Component overlays (like Dialog or the Combo Box drop-down) are no longer rendered outside of the component itself. This causes the following breaking changes to overlay styling:

  • The overlayClass property and the setOverlayClassName method in Flow are gone. Apply a normal class name to the component instead.

  • The vaadin-xyz-overlay (such as vaadin-dialog-overlay) elements can not be targeted with CSS anymore. Refactor any CSS targeting these elements to target the component itself instead (e.g. vaadin-dialog instead of vaadin-dialog-overlay), using the same part names as before. Other CSS selectors are unaffected by this change.

Source code
CSS
vaadin-dialog-overlay::part(content) {}
Source code
CSS
vaadin-dialog::part(content) {}

You’ll find the appropriate selector in the component’s Styling page.

Popover

The Lit/React component’s contentWidth and contentHeight properties have been replaced by width and height.

Rich Text Editor

The on attribute was removed and can no longer be used to target toggled-on buttons. Instead, use the selector ::part(toolbar-button-pressed) to target them with CSS.

Split Layout

The Split Layout component no longer sets overflow:auto on its two child elements. The Scroller component is recommended to make them scrollable on overflow. Alternatively, you can apply it manually with CSS:

Source code
CSS
vaadin-split-layout > * {
  overflow: auto;
}

The SplitterDragendEvent and addSplitterDragendListener have been renamed to SplitterDragEndEvent and addSplitterDragEndListener, respectively.

Tabs / Tab Sheet

The TabsVariant.LUMO_ICON_ON_TOP and TabSheetVariant.LUMO_ICON_ON_TOP theme variants have been removed. Apply the TabVariant.LUMO_ICON_ON_TOP to individual tabs instead.

Text Field

The HasPrefixAndSuffix interface has been removed from the Flow TextField and related components. The components now implement HasPrefix and HasSuffix instead.

Time Picker

The Time Picker no longer uses vaadin-time-picker-combo-box internally. This may affect custom shadow DOM styling of the component.

The TimePickerOverlayElement TestBench element has been removed as the component now uses the native HTML popover mechanism for its drop-down. The getItem and getLastItem methods are now available on TimePickerElement itself.

Tree Grid

Tree Grid’s client-side approach to data loading has been refactored. Instead of requesting data for each hierarchy level separately, it now sends a single request for the visible range, and the server returns the corresponding items as a flat list. On the server side, in turn, it’s now possible to choose how the data provider returns hierarchical data: HierarchyFormat.NESTED (default, each expanded level separately) or HierarchyFormat.FLATTENED (all expanded levels as a flat list). These updates introduce breaking changes, which are described below.

The pageSize property now applies to the entire flattened hierarchy rather than to each level individually as before.

Expanded items are no longer exposed to the client side as a plain array. Instead, the web component receives depth information for each item and uses it to display the data as a tree structure.

As a result, the TreeGridElement#isLoadingExpandedRows TestBench API has been removed. You no longer need to wait for expanded rows specifically since they are loaded in the same request with other rows.

The TreeGridElement#getNumberOfExpandedRows TestBench API has also been removed. Use unit tests instead to verify that exact items are expanded:

Source code
integration test
private TreeGridElement treeGridElement;

@Test
public void shouldHaveSomeRowsExpanded() {
    Assert.assertEquals(2, treeGridElement.getNumberOfExpandedItems());
}
Source code
unit test
private TreeGrid<String> treeGrid;

@Test
public void shouldHaveSomeRowsExpanded() {
    Assert.assertTrue(treeGrid.isExpanded("Item 0"));
    Assert.assertTrue(treeGrid.isExpanded("Item 0-1"));
}

The following section is relevant if your code extends Grid or TreeGrid, or accesses low-level Flow APIs like HierarchicalDataCommunicator.

Low-Level API Changes

The GridArrayUpdater.UpdateQueueData class has been removed, along with related API:

  • The setUpdateQueueData method in GridArrayUpdater has been removed

  • The getUpdateQueueData method in GridArrayUpdater has been removed

  • Parameters that included UpdateQueueData in their type have been removed from all Grid and TreeGrid constructors and methods:

    Source code
    Java
    protected <U extends GridArrayUpdater, B extends DataCommunicatorBuilder<T, U>> Grid(
        Class<T> beanType,
        SerializableBiFunction<UpdateQueueData, Integer, UpdateQueue> updateQueueBuilder,
        B dataCommunicatorBuilder)
    Source code
    Java
    protected <U extends GridArrayUpdater, B extends DataCommunicatorBuilder<T, U>> Grid(
        Class<T> beanType,
        B dataCommunicatorBuilder)
    Source code
    Java
    protected GridArrayUpdater createDefaultArrayUpdater(
        SerializableBiFunction<UpdateQueueData, Integer, UpdateQueue> updateQueueFactory)
    Source code
    Java
    protected GridArrayUpdater createDefaultArrayUpdater()

The TreeGridArrayUpdater interface has also been removed. The GridArrayUpdater interface is now used for both hierarchical and non-hierarchical updates.

The HierarchicalDataCommunicator class in Flow has been fully refactored to use a flat list structure for representing hierarchical data on the client side. Although it still extends the DataCommunicator class, its internal implementation has been completely redesigned to optimize hierarchy rendering and address various bugs. This caused the following breaking changes:

  • Both the HierarchicalCommunicationController and HierarchyMapper concepts have been retired, and all related protected APIs in HierarchicalDataCommunicator have been removed, including such methods as createHierarchyMapper and getHierarchyMapper.

  • The arrayUpdater parameter has been removed from all HierarchicalDataCommunicator constructors. The data communicator now re-renders modified items by making granular Update#set(int index, List items) calls.

  • The protected doUnregister and getPassivatedKeys methods have been removed.

  • The protected setFilter method has been removed. Use the returned consumer of the setDataProvider(HierarchicalDataProvider, Object) method instead.

  • The protected collapse(T item, boolean syncClient) method has been removed. Use the collapse(T item) method instead.

  • The protected expand(T item, boolean syncClient) method has been removed. Use the expand(T item) method instead.

  • The public setRequestedRange and setParentRequestedRange methods have been merged into a single method setViewportRange(int start, int length). Instead of setting ranges separately for each level, this method sets a single range that operates on the flat list of items from all levels.

  • The public confirmUpdate(int id, String parentKey) method has been removed. The confirmUpdate(int id) method is now called instead.

  • The public getParentItem(T item) method has been removed. Use the HierarchicalDataProvider#getParent method instead to get an item’s parent reliably.

  • The public getIndex(T item) and getParentIndex(T item) methods have been removed. To find an item’s index reliably, use a combination of the HierarchicalDataProvider#getItemIndex, HierarchicalDataProvider#getParent, HierarchicalDataCommunicator#buildQuery methods as shown in the example below:

    Source code
    HierarchyFormat.NESTED
    // By default, the data provider implements HierarchyFormat.NESTED,
    // meaning each request returns only the direct children of a parent.
    // In this format, items are identified by their hierarchical path,
    // a list of indexes from the root to the item. This path can then
    // be passed to `TreeGrid#scrollToIndex` to scroll to that item, for
    // example.
    
    public List<Integer> getIndexPath(T item) {
        List<Integer> path = new LinkedList<>();
        do {
            var parent = dataCommunicator.getDataProvider().getParent(item);
            var query = dataCommunicator.buildQuery(parent, 0, Integer.MAX_VALUE);
            var index = dataCommunicator.getDataProvider().getItemIndex(item, query);
            path.addFirst(index);
            item = parent;
        } while (item != null);
        return path;
    }
    HierarchyFormat.FLATTENED

Tree Grid now supports scrolling to a specific item using scrollToItem(T). Unlike scrollToIndex(int…​), this method automatically expands any collapsed parent items before scrolling to the target item.

This feature relies on the getParent(T) and getItemIndex(T, HierarchicalQuery) methods of the HierarchicalDataProvider interface. To use scrollToItem(T), your data provider must implement these methods. The built-in TreeDataProvider already provides full support out of the box.

The following table shows which methods need to be implemented, depending on the data provider type and whether it is in-memory or not:

HierarchicalDataProvider isInMemory() getItemIndex(T, HierarchicalQuery) getParent(T)

HierarchyFormat.NESTED

true

Not required

Required

false

Required

Required

HierarchyFormat.FLATTENED

true

Not required

Required

false

Required

Required

TreeDataProvider

true

Not required

Not required

Upload

The vaadin-upload-file elements representing files in the list now use CSS grid layout instead of flexbox. This may affect custom styling of the element.

The row and info parts have been removed from the vaadin-upload-file element.

Validation

Flow components using validation do not implement HasClientValidation anymore, as such the addClientValidatedEventListener method has been removed. Consider using ValidationStatusChangeEvent to get notified when users enter input that can not be parsed.

Security Configuration Changes

The deprecated VaadinWebSecurity class has been removed from Vaadin 25. Use instead the VaadinSecurityConfigurer base class for your security configuration. Below is an example of this:

Source code
Java
@EnableWebSecurity
@Configuration
@Import(VaadinAwareSecurityContextHolderStrategyConfiguration.class)
public class SecurityConfig {

    @Bean
    SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        /**
         * Delegating the responsibility of general configuration
         * of HTTP security to the VaadinSecurityConfigurer.
         *
         * It's configuring the following:
         * - Vaadin's CSRF protection by ignoring internal framework requests,
         * - default request cache,
         * - ignoring public views annotated with @AnonymousAllowed,
         * - restricting access to other views/endpoints, and
         * - enabling ViewAccessChecker authorization.
         */

        // You can add any possible extra configurations of your own
        // here - the following is just an example:
        http.rememberMe(customizer -> customizer.alwaysRemember(false));

        // Configure your static resources with public access before calling
        // VaadinSecurityConfigurer.vaadin() as it adds final anyRequest matcher
        http.authorizeHttpRequests(auth -> {
            auth.requestMatchers("/admin-only/**").hasAnyRole("admin")
            .requestMatchers("/public/**").permitAll();
        });

        http.with(VaadinSecurityConfigurer.vaadin(), configurer -> {
            // This is important to register your login view to the
            // view access checker mechanism:
            configurer.loginView(LoginView.class);
        });

        return http.build();
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    /**
     * Demo UserDetailsManager which only provides two hardcoded
     * in-memory users and their roles.
     * This shouldn't be used in real-world applications.
     */
    @Bean
    public UserDetailsService userDetailsService(
            PasswordEncoder passwordEncoder) {
        InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
        manager.createUser(User.withUsername("user")
                .password(passwordEncoder.encode("userPass"))
                .roles("USER").build());
        manager.createUser(User.withUsername("admin")
                .password(passwordEncoder.encode("adminPass"))
                .roles("USER", "ADMIN").build());
        return manager;
    }
}
Java

Upgrade To VaadinSecurityConfigurer From VaadinWebSecurity

  1. Add a new method to the security configuration class to provide the security filter chain bean

    Source code
    Java
    @Bean
    public SecurityFilterChain vaadinSecurityFilterChain(HttpSecurity http) throws Exception {
        return http.with(VaadinSecurityConfigurer.vaadin(), vaadin -> {
            ...
        }).build();
    }

    Hint: you can use a static import for VaadinSecurityConfigurer.vaadin().

  2. Move and adapt the code of the configure(HttpSecurity) method into vaadinSecurityFilterChain().

    1. customizations of HttpSecurity placed before super.configure() can be moved before the with(vaadin(), vaadin → {}) instruction.

    2. calls to VaadinWebSecurity methods have related methods in the VaadinSecurityConfigurer.

      Source code
      Java
      @Override
      protected void configure(HttpSecurity http) throws Exception {
          http.authorizeHttpRequests(registry -> {
              registry.requestMatchers("/assets/**").permitAll();
          });
          super.configure(http);
          setLoginView(http, "/login", "/");
      }
      Source code
      Java
      @Bean
      public SecurityFilterChain vaadinSecurityFilterChain(HttpSecurity http) throws Exception {
          http.authorizeHttpRequests(registry -> {
              registry.requestMatchers("/assets/**").permitAll();
          });
          http.with(vaadin(), vaadin -> vaadin.loginView("/login", "/"));
          return http.build();
      }
  3. If configure(WebSecurity web) is overridden you might:

    1. Move the rules in the security filter chain bean definition using HttpSecurity.authorizeRequests() and remove the original method (recommended by Spring, to prevent skipping all other filters in the chain):

      Source code
      Java
      @Override
      protected void configure(WebSecurity web) throws Exception {
          web.ignoring().requestMatchers("/images/**");
      }
      Source code
      Java
      @Bean
      public SecurityFilterChain vaadinSecurityFilterChain(HttpSecurity http) throws Exception {
          http.authorizeHttpRequests(registry -> {
              registry.requestMatchers("/assets/**", "/images/**").permitAll();
          });
          http.with(vaadin(), vaadin -> vaadin.loginView("/login", "/"));
          return http.build();
      }
    2. OR, expose a WebSecurityCustomizer bean by your own and remove the original method

      Source code
      Java
      @Override
      protected void configure(WebSecurity web) throws Exception {
          web.ignoring().requestMatchers("/images/**");
      }
      Source code
      Java
      @Bean
      public WebSecurityCustomizer webSecurityCustomizer() {
          return (web) -> web.ignoring().requestMatchers("/images/**");
      }
  4. If stateless authentication is configured (setStatelessAuthentication(…​)), replace the call using VaadinStatelessSecurityConfigurer

    Source code
    Java
    @Override
    protected void configure(HttpSecurity web) throws Exception {
        //...
        setStatelessAuthentication(http, new SecretKeySpec(Base64.getDecoder().decode(authSecret), JwsAlgorithms.HS256), "com.example.application");
        //...
    }
    Source code
    Java
    @Bean
    public SecurityFilterChain vaadinSecurityFilterChain(HttpSecurity http) throws Exception {
        //...
        http.with(new VaadinStatelessSecurityConfigurer<>(), stateless -> stateless.issuer("com.example.application")
            .withSecretKey()
            .secretKey(new SecretKeySpec(Base64.getDecoder().decode(authSecret), JwsAlgorithms.HS256))
        );
        //...
    }
  5. Remove extends VaadinWebSecurity and import the Vaadin security context holder strategy

    Source code
    Java
    @EnableWebSecurity // should be already present
    @Configuration     // should be already present
    @Import(VaadinAwareSecurityContextHolderStrategyConfiguration.class)
    public class SecurityConfiguration {
    }

Restrict Access By Default For Url-Based Security

URLs not explicitly specified in security configuration changed from being allowed for authenticated users to restricted by default. This requires extra security rules (path matchers) for URLs that were allowed only for authentication users.

Deny Access If Flow Layout Has No Security Annotation

Vaadin Flow layouts now require access annotation (e.g. RolesAllowed) on layout classes. This was added to align with auto-layout default security rules.

TestBench

ComponentTester in UI Unit test has been updated to prove a common void click() method. However, the new method clashes with a similar existing method in AnchorTester and RouterLinkTester that returns an HasElement instance as a result of the navigation. Existing tests that rely on the return type have to migrate to the new navigate() method; if the return value is not used, there is no need for changes.

Because of the change, the com.vaadin.flow.component.html.testbench.ClickHandler class has been removed. The interface, meant to be used with ComponentTester subclasses, should not be needed anymore. In this case, com.vaadin.testbench.unit.Clickable is a valid substitute.

Binder

Binder.validate() implementation has been changed to behave as its Javadoc states. In other words, Binder.validate() no longer fails when bean level validators have been configured but no bean is currently set (i.e. Binder is used in buffered mode).

Server-Side Modality

Dialog has become less strict and allows background requests to server. Vaadin Flow allows to change this behavior if needed through Dialog.setModality(ModalityMode) method.

Form Filler Add-On

The Form Filler add-on has been removed from the Vaadin 25 platform. If your project uses it, you can add it as a separate dependency or get the same functionality with much less code using Spring AI to have the LLM directly populate a Java object that you can then use with e.g. binder.readBean().

Polymer Support

In Vaadin 25, the @polymer/polymer dependency in default package.json is removed by default, if polymer-template module is not found from the project. If the application uses Polymer in add-ons may require to add @NpmPackage(value = "@polymer/polymer", version = "3.5.2") or add an import to package.json explicitly. Details can be found in Ensure Flow works without Polymer in v25.

Frontend Sources Directory

Vaadin uses the {project directory}/src/main/frontend/ directory as the default location for frontend sources. Legacy location {project directory}/frontend/ is deprecated and a warning is shown if it’s used. If you’re using the legacy location, please move your files to the new location, or add the frontendDirectory parameter and point it to the legacy location. Legacy location support will be removed in a future release.

Removed Deprecations

APIs deprecated earlier have now been removed. The following linked GitHub issue lists these removals — Remove deprecated API in Flow 25.0.