Documentation versions (currently viewingVaadin 24)

Upgrading from Vaadin 23

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

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

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

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:

Servlet 6

Vaadin 24 is based on Servlet 6 specifications. It’s compatible with Jakarta EE 10. Vaadin encapsulates the usage of the classes from javax and jakarta packages. Therefore, application code doesn’t need to use servlets, directly. Nevertheless, this is still needed in various cases — like accessing cookies, setting servlet parameters, etc.

Spring Boot 3

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

Java 17

Vaadin 24 requires Java 17 or later. This is dictated by Spring Framework and newer versions of application servers.


Vaadin 24 doesn’t fundamentally change how applications are developed and behave. Still, the upgrade process requires the following essential tasks and testing:


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

Jakarta EE 10

Convert package names to Jakarta EE 10 namespace.

Upgrade Spring

For Spring-based applications, upgrade to Spring Boot 3 or Spring Framework 6, 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 10.

Other Dependencies

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

Verify & Test

Ensure your application is not using deprecated code fragments.

Make sure your application runs well on Java 17 runtime.

Verify that the frontend build works as it should with Vite since webpack is no longer supported.


Portlet and OSGi integrations are not included for two reasons: First, the latest Portlet 3 specification corresponds to Servlet 3, and it doesn’t work with Servlet 6. Second, a Jakarta EE 10 compatible version of OSGi core runtime Apache Felix 8 is under development. The Apache Karaf container is based on Apache Felix and doesn’t have a Jakarta-compatible version.


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


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

Jakarta EE 10 Namespaces

You can use the free tools, Eclipse Transformer and Apache migration tool for the package name conversion.

When applied to a project, they’ll convert Java class imports, manifests, property files, and other resources to use jakarta.* namespace when needed. Conversion instructions are in each tool’s README file.

The last versions of IntelliJ IDEA offer migration refactoring tools, including a Java EE to Jakarta EE package converter. Make sure that the Jakarta specifications in your project have the correct versions. Refer to the full list of Jakarta EE 10 specifications for more information.

Below are a few examples:


Spring Upgrade Instructions

Spring Boot 3 and Spring Framework 6 don’t fundamentally change how applications are developed. The main changes are regarding Jakarta EE 10 namespaces and supported products, the 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 (e.g., Jetty 11, Tomcat 10.1), and many others. Spring has available the Dedicated Migration Guide for Spring-boot 3.0 and the Upgrading to Spring Framework 6.x Guide. You may want to consult them.

To browse a 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 Latest

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



The deprecated VaadinWebSecurityConfigurerAdapter class 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:

public class SecurityConfig extends VaadinWebSecurity {

    public void configure(HttpSecurity http) throws Exception {
         * Delegating the responsibility of general configuration
         * of HTTP security to the superclass.
         * 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:

        // Configure your static resources with public access before calling
        // super.configure(HttpSecurity) as it adds final anyRequest matcher
        http.authorizeHttpRequests(auth -> {
            auth.requestMatchers(new AntPathRequestMatcher("/admin-only/**"))
            .requestMatchers(new AntPathRequestMatcher("/public/**"))

        // This is important to register your login view to the
        // view access checker mechanism:
        setLoginView(http, LoginView.class);

    public void configure(WebSecurity web) throws Exception {
        // Customize your WebSecurity configuration.

    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.
    public UserDetailsService userDetailsService(
            PasswordEncoder passwordEncoder) {
        InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
                .roles("USER", "ADMIN").build());
        return manager;

In this example, AuthenticationManagerBuilder — used in Spring Boot 2 — is replaced by UserDetailsService. Also, http.authorizeRequests().antMatchers() is replaced with http.authorizeHttpRequests(auth → auth.requestMatchers()).

Java Version

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

    <!-- OR: -->

Application Servers

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

CDI 4.0 specification — which is part of Jakarta EE 10 — changes the default value of the bean-discovery-mode attribute to annotated and uses annotated as the default when an empty beans.xml file is found in a deployment. See Jakarta CDI page for more information.

To let the container scan and manage Vaadin components and views when the bean-discovery-mode attribute is not defined and the default is used, you should annotate Vaadin components and views with the com.vaadin.cdi.annotation.CdiComponent annotation to allow Vaadin components to be detected correctly as CDI beans.

As an alternative, you can set bean-discovery-mode=all in the beans.xml file if it’s applicable to your project. However, this is not recommended.

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 also supported and used if the {project directory}/src/main/frontend/ directory doesn’t exist. Although it’s currently optional, if you’re using the legacy location, you should move your files to a new location, as support for this may be removed in the future releases.

Unification with Hilla & Integration with React

Vaadin 24.4 and later versions unify the server and client development approaches known as Vaadin Flow and Hilla respectively. This makes it easier to create hybrid applications, including:

  • unified dependencies for the Vaadin platform, making the project configuration simpler;

  • develop React-based routes and components in Vaadin Flow applications;

  • export Flow components or entire views into frontend views implemented with React; and

  • use unified routing based on React Router.

Hilla & React Dependencies

Vaadin 24 includes React dependencies, such as React, React Router, and Vaadin React-based components provided by Vaadin. These dependencies allow you to start developing immediately with React.

Vaadin React components include free, core components and commercial (i.e., professional) components, which are shipped in the separate npm packages: @vaadin/react-components, which is only free; and @vaadin/react-components-pro, which is only commercial. Vaadin adds both of these packages to package.json if com.vaadin:vaadin artifact is in the project’s configuration:


It adds only @vaadin/react-components if com.vaadin:vaadin-core is used:


Vaadin Flow applications don’t need any changes regarding React or Hilla dependencies management, though the opt-out options are available if you have one of the following cases:

  • Lit is used for frontend development instead of React;

  • having Hilla or React dependencies is undesirable; and

  • you need the legacy vaadin-router, e.g. if your project uses an add-on that needs it.

To opt-out, use the reactEnable configuration parameter. Enabling it excludes React completely from your project and fallbacks to vaadin-router and Lit:


If you don’t need Vaadin’s React components, you can opt-out by excluding the flow-react package in your dependencies. This replaces the @vaadin/react-components and @vaadin/react-components-pro packages in your package.json with @vaadin/* web components:


Excluding Hilla from your project:


The following Hilla dependencies are included in the Vaadin platform dependencies and aren’t needed with Vaadin 24:

  • dev.hilla:hilla-bom

  • dev.hilla:hilla-react

  • dev.hilla:hilla

Frontend Bundles & Hot Deploy Modes

Vaadin chooses using a pre-compiled frontend bundle, or re-building a new bundle, or starting a frontend development server. If it detects Hilla views in a project, it always uses development server — unless one sets the configuration parameter vaadin.frontend.hotdeploy=false. Conversely, for Flow applications it uses frontend bundle — unless one sets the configuration parameter vaadin.frontend.hotdeploy=true.

Default Location for Client-Side Views

Place your React client-side views into src/main/frontend/views/ directory. Vaadin searches for any .tsx or .jsx React views and registers them as React routes if they are present in this directory or subdirectories. See Routing for more details about File-Based Routing.

Polymer Templates

Polymer support has been deprecated since Vaadin 18 was released in November 2020, in favor of faster and simpler Lit templates. The built-in support for Polymer templates has been removed and is only available for Prime and Ultimate customers via an addon. However, a free conversion tool is also available to assist you in converting your Polymer templates to Lit.

Commercial Polymer Template Addon

If you have a Prime or Ultimate subscription, you can continue to use Polymer templates by adding the following dependency to your pom.xml file:


Then you’ll need to update all imports of the PolymerTemplate class to the new coordinates: com.vaadin.flow.component.polymertemplate.PolymerTemplate.

Polymer to Lit Conversion Tool

You can use the free conversion tool to facilitate the migration from Polymer to Lit by automatically converting basic Polymer constructions into their Lit equivalents in Java and JavaScript source files.


The converter covers only basic cases. More advanced cases, such as TypeScript source files or usage of internal Polymer API, should still be converted manually.

See the Polymer-to-Lit converter documentation for more information about limitations and supported transformations.


Regarding usage, run the converter in your project’s root folder as follows:

mvn vaadin:convert-polymer

To convert a project that is based on versions before Vaadin 24, use the following:

mvn com.vaadin:vaadin-maven-plugin:24.4.3:convert-polymer


The converter needs to be configured. It accepts the following properties:


By default, the converter scans all files that match /.js and /.java and then tries to convert them to Lit.

To limit conversion to a specific file or directory, you can use the vaadin.path property like so:

mvn vaadin:convert-polymer -Dvaadin.path=path/to/your/file

The path is always relative to your project’s root folder.


By default, the converter transforms Polymer imports into their Lit 2 equivalents.

If your project is using Lit 1 (i.e., before Vaadin 21), you can use the vaadin.useLit1 flag to enforce Lit 1 compatible imports:

mvn vaadin:convert-polymer -Dvaadin.useLit1

By default, the converter transforms [[prop.sub.something]] expressions into ${this.prop?.sub?.something}.

If your project is using the Vaadin webpack configuration, which doesn’t support the JavaScript optional chaining operator (?.), you can use the vaadin.disableOptionalChaining flag like so:

mvn vaadin:convert-polymer -Dvaadin.disableOptionalChaining

Multiplatform Runtime

Multiplatform Runtime allows the use of legacy Vaadin 7 or 8 framework components in Vaadin Flow applications. The Multiplatform Runtime artifacts remain the same: mpr-v8 and mpr-v7. However, the framework server dependencies now contain an mpr-jakarta postfix:

<!-- Vaadin 8 -->


<!-- Vaadin 7 -->

Other legacy framework dependencies have the same names, but transitive dependencies to vaadin-server artifacts must be detected and excluded. Consult Exclude Incompatible Framework 8 Dependency for further details.

Maven & Gradle Plugins

Ensure that the Maven plugins which are explicitly defined in your project, are compatible with Java 17. For example, the nexus-staging-maven-plugin` requires a minimum version of 1.6.13. Maven version 3.5 and later support Java 17, but avoid using 3.8.2 or 3.8.3. They break redeploy in Jetty.

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

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

./gradlew wrapper --gradle-version 8.6

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

SLF4J 2.0

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.

Line-Awesome Icons Set Library

Vaadin 23 starter projects used the Line Awesome icon set. If you need those icons, you should use the Line Awesome add-on with Vaadin 24. This add-on doesn’t require building a new bundle, which means your application will benefit from the pre-compiled frontend bundle feature.

New Types Definition

Vaadin might ask to add new types definitions to the types.d.ts file, if it detects customized content in it. If so, the first build after the upgrade will fail, and show instructions on how to correct it in the server logs.

Breaking Changes in Vaadin Components

Vaadin components have several breaking changes related to upgrading to the latest version.

Shrinking Badges

Badges no longer shrink by default. This can be overridden globally with CSS [theme~="badge"] { flex-shrink:1; }, or for specific instances with badge.getStyle().set("flex-shrink", "1"), or with layout.setFlexShrink(badge, 1).

Shrinking Buttons

Buttons also no longer shrink by default. This also can be overridden globally, but with CSS vaadin-button { flex-shrink:1; }, or for specific instances with btn.getStyle().set("flex-shrink", "1") or with layout.setFlexShrink(btn, 1).


The CheckboxGroup::setItemLabelGenerator no longer clears the current selection. The CheckboxGroup.clear() can be used to clear values, separately.


The BlurNotifier<CustomField> type signature was corrected to BlurNotifier<CustomField<T>>.

Date Picker

Date Picker now uses the ISO 8601 date format (i.e., yyyy-mm-dd) as fallback for unsupported locales — instead of mm-dd-yyyy. Time Picker no longer automatically adjusts values to fit minimum and maximum constraints.

Number Field

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


RichTextEditor::setvalue and getValue now use HTML format by default, instead of Delta. Applications using the Delta format must be refactored to use the RichTextEditor.asDelta() API (e.g. rte.asDelta().getValue() and binder.forField(rte.asDelta())). To help avoid using the wrong setter, RichTextEditor.setValue(String) now throws an exception if the value looks like it’s in Delta format (i.e., it starts with a [ or { bracket). To set an HTML value starting with the above characters, either wrap the value in an HTML tag, or use the RichTextEditor.asHtml() API, which doesn’t check for them.

Margins for Headings

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; }

Removed Flow Component APIs

View Removed or Renamed Flow APIs

The following Vaadin component APIs have been removed or renamed in Flow:

  • The Generated[ComponentName] classes have been removed. Extend the normal component classes instead when extending components.

    The following event classes were introduced in V23.3 as replacements for the ones in the generated classes: DatePicker.OpenedChangeEvent; DatePicker.InvalidChangeEvent; Dialog.OpenedChangeEvent; Notification.OpenedChangeEvent; and SplitLayout.SplitterDragendEvent.

    The generic type parameters in these events — introduced in V23.3 for backward compatibility — was removed in V24.

  • Button.setDisabled() was removed in favor of setEnabled(false).

  • Charts HTMLLabels and HTMLLabelItem APIs were removed — they were broken — in favor of Configuration.addLabel or Configuration.setLabels with AnnotationItemLabel objects instead of HTMLLabel. Coordinate information for the label can be provided using AnnotationItemLabel.setPoint.

  • Checkbox::setLabelAsHtml was removed in favor of renderers (i.e., setRenderer).

  • CheckboxGroup and RadioButtonGroup no longer implement the HasComponents and HasItemComponents interfaces and the following related methods have been removed: — add(Component…​), add(Collection), and add(String); — remove(Component…​), remove(Collection), and removeAll(); — addComponentAtIndex(int, Component); — addComponentAsFirst(Component); — addComponents(T, Component…​); — prependComponents(T, Component…​); and — getItemPosition(T).

  • ContextMenu clickListener API was removed — it wasn’t working. Apply instead click listeners to the menu’s target component if needed.

  • CustomFieldI81n::parseValue and CustomFieldI18n::formatValue were moved to CustomField::parseValue and CustomField::formatValue.

  • DatePickerI18n setters and getters for clear, .calendar, and .week were removed since it was unused.

  • FlexLayout.getFlexDirection(HasElement elementContainer) overload was removed — it was pointless — in favor of getFlexDirection().

  • Grid::setHeightByRows was removed in favor of Grid::setAllRowsVisible.

  • Grid.addColumn(renderer, sortingProperties) was removed in favor of addColumn(renderer).setSortProperty(sortingProperties).

  • Grid.ItemClickEvent and ItemDoubleClickEvent without columnId were removed.

  • Grid::findInShadowRoot was removed.

  • Grid::setVerticalScrollingEnabled was removed: it wasn’t working.

  • Map.Coordinate::fromLonLat was removed as unnecessary since the default coordinate system is now EPSG:4326, and new Coordinate(x,y) is sufficient.

  • Map APIs that used float values now use double.

  • NumberField::setMaxLength, setPattern, and setPreventInvalidInput were removed because they didn’t work.

  • NumberField::setHasControls was renamed setStepButtonsVisible.

  • RichTextEditor(String value) constructor was removed in favor of RichTextEditor(), followed by setValue(String value).

  • Select(T…​ items) constructor was removed in favor of Select(String label, T…​ items).

  • SplitLayout.IronResizeEvent was removed as part of a migration away from Polymer.

  • Tabs no longer implements the HasComponents interface, with the following APIs removed or deprecated: — add(Collection) was removed in favor of add(Tab…​); — remove(Collection) was removed in favor of remove(Tab…​); — add(String) was removed; — indexOf(Component) was deprecated in favor of indexOf(Tab); — add(Component) was deprecated in favor of add(Tab); — remove(Component) was deprecated in favor of remove(Tab); — replace(Component, Component) was deprecated in favor of replace(Tab, Tab); — getComponentAt(int) was deprecated in favor of getTabAt(int); — addComponentAtIndex(int, Component) was deprecated in favor of addTabAtIndex(int, Tab); and — addComponentAsFirst(Component) was deprecated in favor of addTabAsFirst(Tab).

  • TemplateRenderer public API was removed in favor of LitRenderer.

  • TextField::setPreventInvalidInput was removed in favor of setAllowedCharPattern.

  • TimePicker.setMin(String) and setMax(String) were removed in favor of setMin(LocalTime) and setMax(LocalTime).

  • Upload SelectedChangeEvent(Tabs source, boolean fromClient) overload was removed in favor of SelectedChangeEvent(Tabs source, Tab previousTab, boolean fromClient).

  • UploadI18n::setCancel and UploadI18n::getCancel were removed since they were unused.

Web Component APIs

Click and read if using Vaadin Components without Flow.

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

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

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

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

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

  • Deprecated xxxElement legacy class aliases (e.g., GridElement) have been removed. Use the plain component classes instead (e.g., 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 was removed in favor of setAllowedCharPattern.

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

Update Your Component Styling

Click and read if styled Vaadin Components.

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

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

The instructions below are based on the old approach, which is the faster route for upgrading. The new styling approach is described in the Styling section of the documentation.


The summary (or header) of Accordion panels has been refactored into a separate custom element type, slotted into the panel component.

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

Parts that were previously inside vaadin-accordion-panel 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 reverted with

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

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

: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’s items, vaadin-context-menu-item, no longer extend the vaadin-item element, and as such no longer inherit styling from it. The items can be styled separately by targeting vaadin-context-menu-item instead of vaadin-item.

Combo Box

Flow only: items rendered with a ComponentRenderer no longer render their contents wrapped into a <flow-component-renderer> element. This may affect the rendering of the contents, especially in terms of scaling, as they are now rendered directly into a <vaadin-combo-box-item>.


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 {...}

The Grid inside the non-Flow version of the CRUD 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"] {...}


The summary (or header) 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"] {...}


Flow only: the contents of a Dialog are no longer rendered into a <div> inside a <flow-component-renderer> element. This may affect the rendering of the contents, especially in terms of scaling, as they are now rendered directly into a slot in the vaadin-notification-card root element. If desired, contents can be wrapped in a layout component, a Div, or any other container element with the desired sizing, etc. In a draggable Dialog, you’ll need to add the draggable and draggable-leaf-only class names to these custom wrappers in order for them to work as drag handles.


Flow only: inline editor components, and cell contents rendered with a ComponentRenderer, are no longer rendered wrapped into a <flow-component-renderer> element. This may affect the rendering of the contents, especially in terms of scaling, as they are now rendered directly into a slot in the vaadin-dialog-overlay web component.


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 (i.e. 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 need to be styled separately 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"]) {...}


Flow only: components placed into the Notification are now longer rendered into a <div> inside a <flow-component-renderer> element. This may affect the rendering of the contents, especially in terms of scaling, as they are now rendered directly into a slot in the vaadin-notification-card web component.


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) {...}

Field Validation Changes

Vaadin 24 introduces several major changes in field component validation, addressing various issues and UX inconsistencies that have been reported in previous versions.

Binder Considers Component Constraints

Every field component provides its own set of constraints, such as required, minlength, pattern, etc.

Before: When using Binder, component constraints were ignored in earlier versions. They could still affect the client-side invalid state without reflecting this to the server.

After: In the new version, component constraints are integrated into Binder validation. Binder checks the value against constraints before custom validators. The only exception is the required constraint. It doesn’t participate in Binder validation since Binder provides its own asRequired validator.

Known Issues: It’s currently not possible to configure custom error messages for constraints (flow-components#4618).

Also, it’s currently not possible to revert Binder’s behavior to ignore constraints as before, except by removing the constraints from the component, entirely (flow#17178).

Blur Triggers Server-Side Validation

Before: Both constraint and Binder validation were previously only triggered on ValueChangeEvent, while client-side validation was completely disregarded.

After: The server-side validation timing is now aligned with client-side validation, meaning that whenever the web component validation occurs, it triggers the corresponding validation in the Flow component. In practice, this means that the server-side validation also is performed on blur (i.e., when the component loses focus).

Known Issues: The component validates on blur even when the user leaves it without typing anything. This behavior results from the web component, which validates on blur to detect possible bad input (web-components#6146).

The server-side validation can be triggered more frequently than necessary. For example, if you edit a Text Field and then blur, both value-changed and blur events can fire at the same time, resulting in double validation in a single round-trip (flow-components#4390).

Bad Input Invalidates Component

Date Picker, Integer Field, and some other similar components only accept user input that can be parsed as a LocalDate, Integer, etc. Otherwise, the value on the server falls back to null.

Before: Previously, entering bad input didn’t invalidate the component because the server treated this case as an empty value.

After: The server is now aware if the user has entered any input. When the input cannot be parsed, it’s considered bad input, causing constraint validation to fail once the user presses Enter or removes focus from the component.

Manual Validation Mode

There are applications that require a totally custom validation logic that is beyond the capabilities of validation tools provided by Vaadin. For such applications, Vaadin 24.2 introduces a manual validation mode. This mode disables the component’s built-in validation, allowing developers to have manual control over the component’s invalid state and error messages.

Below is an example of a custom implementation of the required validation performed on ValueChangeEvent:

TextField textField = new TextField();
textField.addValueChangeListener(event -> {
    if (Objects.equals(event.getValue(), "")) {
        textField.setErrorMessage("The field is required.");
    } else {

Removed Deprecations

APIs that were deprecated earlier have now been removed. The following linked GitHub issue lists these removals: