Upgrading from Vaadin 23
- Overview
- Limitations
- Preparation
- Jakarta EE 10 Namespaces
- Spring Upgrade Instructions
- Java Version
- Application Servers
- Frontend Sources Directory
- Unification with Hilla & Integration with React
- Polymer Templates
- Multiplatform Runtime
- Maven & Gradle Plugins
- SLF4J 2.0
- Line-Awesome Icons Set Library
- New Types Definition
- Breaking Changes in Vaadin Components
- Field Validation Changes
- Removed Deprecations
This guide goes through the changes you’ll need to make in your applications when upgrading from Vaadin 23 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 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
andjakarta
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.
Overview
Vaadin 24 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 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.
Limitations
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.
Preparation
Upgrade the Vaadin version in the pom.xml
and gradle.properties
files to the latest release like so:
<vaadin.version>24.5.8</vaadin.version>
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:
<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 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.
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:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.0.0</version>
</parent>
Deprecation
The deprecated VaadinWebSecurityConfigurerAdapter
class was removed since Spring no longer includes 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 {
@Override
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:
http.rememberMe().alwaysRemember(false);
// 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/**"))
.hasAnyRole("admin")
.requestMatchers(new AntPathRequestMatcher("/public/**"))
.permitAll();
});
super.configure(http);
// This is important to register your login view to the
// view access checker mechanism:
setLoginView(http, LoginView.class);
}
@Override
public void configure(WebSecurity web) throws Exception {
// Customize your WebSecurity configuration.
super.configure(web);
}
@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;
}
}
In the example here, AuthenticationManagerBuilder
— used in Spring Boot 2 — is replaced by UserDetailsService
. Also, http.authorizeRequests().antMatchers()
is replaced with http.authorizeHttpRequests(auth → auth.requestMatchers())
.
Spring Security
If the application is using Spring Security 5, the default behavior is for the SecurityContext
to be saved automatically to the SecurityContextRepository
using the SecurityContextPersistenceFilter
.
In Spring Security 6, users now must explicitly save the SecurityContext
to the SecurityContextRepository
.
You can return the old behavior by adding to configuration(HttpSecurity)
the line http.securityContextsecurityContext) → securityContext.requireExplicitSave(false;
.
For more information see Servlet Migrations - Session Management.
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>
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
to allow them 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 isn’t recommended.
Frontend Sources Directory
Vaadin uses the {project directory}/src/main/frontend/
directory as the default location for frontend sources. The legacy location {project directory}/frontend/
is still 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:
-
unify dependencies for the Vaadin, 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 includes free, core components and commercial (i.e., professional) components. These components 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 the com.vaadin:vaadin
artifact is in the project’s configuration:
<dependency>
<groupId>com.vaadin</groupId>
<artifactId>vaadin</artifactId>
</dependency>
It adds only @vaadin/react-components
if com.vaadin:vaadin-core
is used:
<dependency>
<groupId>com.vaadin</groupId>
<artifactId>vaadin-core</artifactId>
</dependency>
Vaadin Flow applications don’t need any changes regarding React or Hilla dependencies management. However, the opt-out options are available for 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).
Opting-Out of React
To opt-out, change both reactEnable
plugin configuration parameter and vaadin.react.enable
configuration property to false
.
Maven plugin configuration parameter in the vaadin-maven-plugin
:
<plugin>
<groupId>com.vaadin</groupId>
<artifactId>vaadin-maven-plugin</artifactId>
<configuration>
<reactEnable>false</reactEnable>
</configuration>
</plugin>
Below is the configuration property with Spring Boot application properties:
vaadin.react.enable=false
Disabling it excludes React completely from your project and fallbacks to vaadin-router
and Lit.
Two Places for Same Property
Two properties exists mainly to support the development and production modes. reactEnable
is effective for both; vaadin.react.enable
, though, is effective only for development.
Production package and bundle is built by running the Vaadin Maven and Gradle plugin with build-frontend
goal. The reactEnable
plugin configuration parameter value is contained in the build information file inside the package, which is used as a default value in runtime.
Configuration property vaadin.react.enable
overrides the default value in the production package at runtime. However, it doesn’t change the production bundle content.
Running an application with the vaadin.react.enable
in production mode means possibly using a different value than what was used when the production package was built. Both properties should have the same value to avoid confusion as to which is effective.
Opting-Out of React with Spring
System property has priority over application property. When running a Spring application with Maven, you may need to wrap system property in -Dspring-boot.run.jvmArguments=vaadin.react.enable=false"
.
Simplified Maintenance
To make it easier to keep both properties the same, Spring supports Automatic Property Expansion with Maven and with Gradle. Please follow the instructions in the Spring documentation to enable this feature.
When running with Maven and using spring-boot-starter-parent
, property value can have @reactEnable@
, which is replaced with the value of the reactEnable
Maven project property. Otherwise, please follow the Spring documentation to see how to enable resources
filtering and set maven-resources-plugin
configurations.
vaadin.react.enable=@reactEnable@
<properties>
<reactEnable>false</reactEnable>
</properties>
<plugin>
<groupId>com.vaadin</groupId>
<artifactId>vaadin-maven-plugin</artifactId>
<configuration>
<reactEnable>${reactEnable}</reactEnable>
</configuration>
</plugin>
Opting-Out of React without Spring
When Spring is not used, set vaadin.react.enable
system property or react.enable
initialization parameter.
Opting-Out of React Components
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:
<dependency>
<groupId>com.vaadin</groupId>
<artifactId>vaadin</artifactId>
<exclusions>
<exclusion>
<groupId>com.vaadin</groupId>
<artifactId>flow-react</artifactId>
</exclusion>
</exclusions>
</dependency>
This example exclude Hilla from a project when using Maven:
<dependency>
<groupId>com.vaadin</groupId>
<artifactId>vaadin</artifactId>
<exclusions>
<exclusion>
<groupId>com.vaadin</groupId>
<artifactId>hilla</artifactId>
</exclusion>
<exclusion>
<groupId>com.vaadin</groupId>
<artifactId>hilla-dev</artifactId>
</exclusion>
</exclusions>
</dependency>
This example is for when you’re using Gradle:
buildscript {
configurations.classpath {
exclude group: 'com.vaadin', module: 'hilla-engine-core'
}
}
dependencies {
implementation ("com.vaadin:vaadin") {
exclude group: 'com.vaadin', module: 'hilla-dev'
}
}
hilla.active=false
The following Hilla dependencies are included in Vaadin. You don’t need to include these explicitly:
-
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 uses development server — unless you set the configuration parameter, vaadin.frontend.hotdeploy=false
. Conversely, for Flow applications it uses frontend bundle — unless you set the configuration parameter, vaadin.frontend.hotdeploy=true
.
Default Location for Client-Side Views
Place your React client-side views in the src/main/frontend/views/
directory. Vaadin searches for any .tsx
or .jsx
React views. It registers them as React routes if they’re 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:
<dependency>
<groupId>com.vaadin</groupId>
<artifactId>flow-polymer-template</artifactId>
</dependency>
Then you’ll need to update all imports of the PolymerTemplate
classes 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 converting automatically basic Polymer constructions into their Lit equivalents in Java and JavaScript source files.
Limitations
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.
Usage
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.5.8:convert-polymer
Configuring
The converter needs to be configured. It accepts the following properties:
-Dvaadin.path=path/to/your/file
-
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.
-Dvaadin.useLit1
-
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
-Dvaadin.disableOptionalChaining
-
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 thevaadin.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 -->
<dependency>
<groupId>com.vaadin</groupId>
<artifactId>vaadin-server-mpr-jakarta</artifactId>
<version>8.19.0</version>
</dependency>
<dependency>
<groupId>com.vaadin</groupId>
<artifactId>vaadin-compatibility-server-mpr-jakarta</artifactId>
<version>8.19.0</version>
</dependency>
<!-- Vaadin 7 -->
<dependency>
<groupId>com.vaadin</groupId>
<artifactId>vaadin-server-mpr-jakarta</artifactId>
<version>7.7.37</version>
</dependency>
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 any 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 you 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. These are covered in the sections that follow.
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)
.
CheckboxGroup
The CheckboxGroup::setItemLabelGenerator
no longer clears the current selection. The CheckboxGroup.clear()
can be used to clear values, separately.
BlurNotifier
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 adjusts automatically 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
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
; andSplitLayout.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 ofsetEnabled(false)
. -
Charts
HTMLLabels
andHTMLLabelItem
APIs were removed — they were broken — in favor ofConfiguration.addLabel
orConfiguration.setLabels
withAnnotationItemLabel
objects instead ofHTMLLabel
. Coordinate information for the label can be provided usingAnnotationItemLabel.setPoint
. -
Checkbox::setLabelAsHtml
was removed in favor of renderers (i.e.,setRenderer
). -
CheckboxGroup
andRadioButtonGroup
no longer implement theHasComponents
andHasItemComponents
interfaces and the following related methods have been removed: —add(Component…)
,add(Collection)
, andadd(String)
; —remove(Component…)
,remove(Collection)
, andremoveAll()
; —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
andCustomFieldI18n::formatValue
were moved toCustomField::parseValue
andCustomField::formatValue
. -
DatePickerI18n
setters and getters forclear
,.calendar
, and.week
were removed since it was unused. -
FlexLayout.getFlexDirection(HasElement elementContainer)
overload was removed — it was pointless — in favor ofgetFlexDirection()
. -
Grid::setHeightByRows
was removed in favor ofGrid::setAllRowsVisible
. -
Grid.addColumn(renderer, sortingProperties)
was removed in favor ofaddColumn(renderer).setSortProperty(sortingProperties)
. -
Grid.ItemClickEvent
andItemDoubleClickEvent
withoutcolumnId
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, andnew Coordinate(x,y)
is sufficient. -
Map
APIs that usedfloat
values now usedouble
. -
NumberField::setMaxLength
,setPattern
, andsetPreventInvalidInput
were removed because they didn’t work. -
NumberField::setHasControls
was renamedsetStepButtonsVisible
. -
RichTextEditor(String value)
constructor was removed in favor ofRichTextEditor()
, followed bysetValue(String value)
. -
Select(T… items)
constructor was removed in favor ofSelect(String label, T… items)
. -
SplitLayout.IronResizeEvent
was removed as part of a migration away from Polymer. -
Tabs
no longer implements theHasComponents
interface, with the following APIs removed or deprecated: —add(Collection)
was removed in favor ofadd(Tab…)
; —remove(Collection)
was removed in favor ofremove(Tab…)
; —add(String)
was removed; —indexOf(Component)
was deprecated in favor ofindexOf(Tab)
; —add(Component)
was deprecated in favor ofadd(Tab)
; —remove(Component)
was deprecated in favor ofremove(Tab)
; —replace(Component, Component)
was deprecated in favor ofreplace(Tab, Tab)
; —getComponentAt(int)
was deprecated in favor ofgetTabAt(int)
; —addComponentAtIndex(int, Component)
was deprecated in favor ofaddTabAtIndex(int, Tab)
; and —addComponentAsFirst(Component)
was deprecated in favor ofaddTabAsFirst(Tab)
. -
TemplateRenderer
public API was removed in favor ofLitRenderer
. -
TextField::setPreventInvalidInput
was removed in favor ofsetAllowedCharPattern
. -
TimePicker.setMin(String)
andsetMax(String)
were removed in favor ofsetMin(LocalTime)
andsetMax(LocalTime)
. -
Upload
SelectedChangeEvent(Tabs source, boolean fromClient)
overload was removed in favor ofSelectedChangeEvent(Tabs source, Tab previousTab, boolean fromClient)
. -
UploadI18n::setCancel
andUploadI18n::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
andvaadin-radio-button
must be set using thelabel
property, as the default slot has been removed. -
vaadin-confirm-dialog.cancel
and.reject
properties were renamed.cancelButtonVisible
and.rejectButtonVisible
. -
vaadin-number-field
propertyhas-controls
was renamedstep-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
, andtrendind-down
. -
notifyResize
andupdateStyles
methods were removed from various components as obsolete. -
preventInvalidInput
in text input fields was removed in favor ofsetAllowedCharPattern
. -
The read-only
theme
property was removed. Use instead thetheme
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.
Accordion
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>
.
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 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"] {...}
Details
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"] {...}
Dialog
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.
Grid
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.
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"]) {...}
Menu Bar
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"]) {...}
Notification
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.
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) {...}
Field Validation Changes
Vaadin 24 introduced several major changes in field component validation, addressing various issues and UX inconsistencies that have been reported in previous versions. These are listed and described in the sections that follow.
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 fully customized 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.setManualValidation(true);
textField.addValueChangeListener(event -> {
if (Objects.equals(event.getValue(), "")) {
textField.setInvalid(true);
textField.setErrorMessage("The field is required.");
} else {
textField.setInvalid(false);
}
});