> Markdown version of [Upgrading Guide](https://vaadin.com/docs/v24/upgrading). Section index: [llms.txt](https://vaadin.com/docs/v24/llms.txt)

# Upgrading from Vaadin 23

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](/docs/v23/upgrading/essential-steps) 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.

## <a id="overview"></a>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](https://github.com/vaadin/platform/releases).

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

## <a id="limitations"></a>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](https://felix.apache.org/documentation/index.html) is under development. The [Apache Karaf](https://karaf.apache.org/) container is based on Apache Felix and doesn’t have a Jakarta-compatible version.

## <a id="preparation"></a>Preparation

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

**pom.xml**

```xml
<vaadin.version>24.10.3</vaadin.version>
```

**gradle.properties**

```properties
vaadinVersion=24.10.3
```

See the [list of releases on GitHub](https://github.com/vaadin/platform/releases) for the latest one.

## <a id="jakarta-ee-10-namespaces"></a>Jakarta EE 10 Namespaces

You can use the free tools, [Eclipse Transformer](https://github.com/eclipse/transformer) and [Apache migration tool](https://github.com/apache/tomcat-jakartaee-migration) 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](https://www.jetbrains.com/help/idea/2022.2/migrate.html) 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](https://jakarta.ee/release/10/) for more information.

Below are a few examples:

pom.xml

```xml
<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>
```

## <a id="spring-upgrade-instructions"></a>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](https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-3.0-Migration-Guide) and the [Upgrading to Spring Framework 6.x Guide](https://github.com/spring-projects/spring-framework/wiki/Upgrading-to-Spring-Framework-6.x). You may want to consult them.

To browse a full list of changes, see the [Spring-boot 3.0 Release Notes](https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-3.0-Release-Notes) and the [What’s New in Spring Framework 6.x](https://github.com/spring-projects/spring-framework/wiki/What%27s-New-in-Spring-Framework-6.x) page.

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

### <a id="upgrade-spring-to-latest"></a>Upgrade Spring to Latest

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

pom.xml

```xml
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>3.0.0</version>
</parent>
```

### <a id="deprecation"></a>Deprecation

The deprecated `VaadinWebSecurityConfigurerAdapter` class was removed since Spring no longer includes the `WebSecurityConfigurerAdapter` class. Use instead the `VaadinSecurityConfigurer` class or `VaadinWebSecurity` base class (deprecated since V24.9) for your security configuration. Below is an example of this:

**VaadinSecurityConfigurer**

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

**VaadinWebSecurity (deprecated since V24.9)**

```java
@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())`.

### <a id="spring-security"></a>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 `SecurityFilterChain securityFilterChain(HttpSecurity http)` (or `configure(HttpSecurity http)` with `VaadinWebSecurity`) the line `http.securityContext ( (securityContext) → securityContext.requireExplicitSave( false ) );`.

For more information see [Servlet Migrations - Session Management](https://docs.spring.io/spring-security/reference/5.8/migration/servlet/session-management.html#_require_explicit_saving_of_securitycontextrepository).

## <a id="java-version"></a>Java Version

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

**Maven**

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

**Gradle (Kotlin DSL)**

```kotlin
plugins {
    java
}

java {
    toolchain {
        languageVersion = JavaLanguageVersion.of(17)
    }
}
```

**Gradle (Groovy DSL)**

```groovy
plugins {
    id 'java'
}

java {
    toolchain {
        languageVersion = JavaLanguageVersion.of(17)
    }
}
```

## <a id="application-servers"></a>Application Servers

Before migrating, find the corresponding version of the Jakarta EE 10-compatible application server used in your project. See [Jakarta Compatible Products](https://jakarta.ee/compatibility/) 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](https://jakarta.ee/specifications/cdi/4.0/) 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.

## <a id="frontend-sources-directory"></a>Frontend Sources Directory (since V24.4)

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.

## <a id="unification-with-hilla-integration-with-react"></a>Unification with Hilla & Integration with React (since V24.4)

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](https://reactjs.org/)-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](https://reactrouter.com/en/main).

### <a id="hilla-react-dependencies"></a>Hilla & React Dependencies

Vaadin 24 includes React dependencies, such as [React](https://www.npmjs.com/package/react), [React Router](https://www.npmjs.com/package/react-router), and [Vaadin React-based components](https://github.com/vaadin/react-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:

pom.xml

```xml
<dependency>
	<groupId>com.vaadin</groupId>
	<artifactId>vaadin</artifactId>
</dependency>
```

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

pom.xml

```xml
<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).

#### <a id="opting-out-of-react"></a>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`:

pom.xml

```xml
<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:

application.properties

```terminal
vaadin.react.enable=false
```

Disabling it excludes React completely from your project and fallbacks to `vaadin-router` and Lit.

##### <a id="two-places-for-same-property"></a>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.

##### <a id="opting-out-of-react-with-spring"></a>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"`.

###### <a id="simplified-maintenance"></a>Simplified Maintenance

To make it easier to keep both properties the same, Spring supports [Automatic Property Expansion with Maven](https://docs.spring.io/spring-boot/how-to/properties-and-configuration.html#howto.properties-and-configuration.expand-properties.maven) and [with Gradle](https://docs.spring.io/spring-boot/how-to/properties-and-configuration.html#howto.properties-and-configuration.expand-properties.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](https://docs.spring.io/spring-boot/how-to/properties-and-configuration.html#howto.properties-and-configuration.expand-properties.maven) to see how to enable `resources` filtering and set `maven-resources-plugin` configurations.

application.properties

```terminal
vaadin.react.enable=@reactEnable@
```

pom.xml — project properties

```xml
<properties>
    <reactEnable>false</reactEnable>
</properties>
```

pom.xml — build configuration

```xml
<plugin>
   <groupId>com.vaadin</groupId>
   <artifactId>vaadin-maven-plugin</artifactId>
   <configuration>
       <reactEnable>${reactEnable}</reactEnable>
   </configuration>
</plugin>
```

##### <a id="opting-out-of-react-without-spring"></a>Opting-Out of React without Spring

When Spring is not used, set `vaadin.react.enable` [system property](https://vaadin.com/docs/v24/flow/configuration/properties.md#system-properties) or `react.enable` [initialization parameter](https://vaadin.com/docs/v24/flow/configuration/properties.md#servlet-initialization-parameters).

#### <a id="opting-out-of-react-components"></a>Opting-Out of React Components

If you don’t need Vaadin’s [React components](https://github.com/vaadin/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:

**Maven**

```xml
<dependency>
	<groupId>com.vaadin</groupId>
	<artifactId>vaadin</artifactId>
	<exclusions>
		<exclusion>
			<groupId>com.vaadin</groupId>
			<artifactId>flow-react</artifactId>
		</exclusion>
	</exclusions>
</dependency>
```

**Gradle**

```groovy
dependencies {
    implementation ("com.vaadin:vaadin") {
        exclude group: 'com.vaadin', module: 'flow-react'
    }
}
```

This example exclude Hilla from a project when using Maven:

pom.xml

```xml
<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:

build.gradle

```groovy
buildscript {
    configurations.classpath {
        exclude group: 'com.vaadin', module: 'hilla-engine-core'
    }
}

dependencies {
    implementation ("com.vaadin:vaadin") {
        exclude group: 'com.vaadin', module: 'hilla-dev'
    }
}
```

gradle.properties

**Gradle (Groovy DSL)**

```properties
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`

### <a id="frontend-bundles-hot-deploy-modes"></a>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`.

### <a id="default-location-for-client-side-views"></a>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 ](https://vaadin.com/docs/v24/hilla/guides/routing.md)for more details about File-Based Routing.

## <a id="polymer-templates"></a>Polymer Templates

Polymer support has been deprecated since Vaadin 18 was released in November 2020, in favor of faster and simpler [Lit templates](https://vaadin.com/docs/v24/flow/create-ui/templates.md). 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.

### <a id="commercial-polymer-template-addon"></a>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:

pom.xml

```xml
<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`.

#### <a id="limitations-2"></a>Limitations

Even with the PolymerTemplate Addon, you cannot use the Polymer-based TemplateRenderer; instead [LitRenderer](https://vaadin.com/docs/v24/components/grid/renderers.md#using-lit-renderers#) is required. Additionally, if the Polymer template relies on Vaadin component imports and APIs, these may need to be updated.

### <a id="polymer-to-lit-conversion-tool"></a>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.

#### <a id="limitations-3"></a>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](https://github.com/vaadin/flow/tree/master/flow-polymer2lit) for more information about limitations and supported transformations.

#### <a id="usage"></a>Usage

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

**Maven**

```terminal
mvn vaadin:convert-polymer
```

**Groovy**

```groovy
./gradlew vaadinConvertPolymer
```

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

**Maven**

```terminal
mvn com.vaadin:vaadin-maven-plugin:24.10.3:convert-polymer
```

`build.gradle`

**Groovy**

```groovy
buildscript {
  repositories {
    classpath 'com.vaadin:flow-gradle-plugin:24.10.3'
  }
}
```

#### <a id="configuring"></a>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:

  **Maven**

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

  **Groovy**

  ```terminal
  ./gradlew vaadinConvertPolymer -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:

  **Maven**

  ```terminal
  mvn vaadin:convert-polymer -Dvaadin.useLit1
  ```

  **Groovy**

  ```terminal
  ./gradlew vaadinConvertPolymer -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 the `vaadin.disableOptionalChaining` flag like so:

  **Maven**

  ```terminal
  mvn vaadin:convert-polymer -Dvaadin.disableOptionalChaining
  ```

  **Groovy**

  ```terminal
  ./gradlew vaadinConvertPolymer -Dvaadin.disableOptionalChaining
  ```

## <a id="multiplatform-runtime"></a>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:

pom.xml

```xml
<!-- 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](https://vaadin.com/docs/v24/tools/mpr/introduction/1-maven-v8.md#exclude-incompatible-framework-8-dependency) for further details.

## <a id="maven-gradle-plugins"></a>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](https://docs.gradle.org/8.6/release-notes.html) 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:

```terminal
./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.

## <a id="slf4j-2-0"></a>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](https://www.slf4j.org/news.html) for more information.

## <a id="line-awesome-icons-set-library"></a>Line-Awesome Icons Set Library

Vaadin 23 starter projects used the [Line Awesome](https://icons8.com/line-awesome) icon set. If you need those icons, you should use the [Line Awesome add-on](https://vaadin.com/directory/component/line-awesome-icons-for-vaadin) 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.

## <a id="new-types-definition"></a>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.

## <a id="breaking-changes-in-vaadin-components"></a>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.

### <a id="shrinking-badges"></a>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)`.

### <a id="shrinking-buttons"></a>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)`.

### <a id="checkboxgroup"></a>CheckboxGroup

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

### <a id="blurnotifier"></a>BlurNotifier

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

### <a id="date-picker"></a>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.

### <a id="number-field"></a>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; }`.

### <a id="richtexteditor"></a>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.

### <a id="margins-for-headings"></a>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:

styles.css

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

### <a id="removed-flow-component-apis"></a>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.

### <a id="web-component-apis"></a>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*.

### <a id="update-your-component-styling"></a>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.

### <a id="accordion"></a>Accordion

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

`vaadin-accordion-panel.css`

```css
[part="summary"] {...}
```

`vaadin-accordion-panel.css`

```css
::slotted(vaadin-accordion-heading) {...}
```

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

`vaadin-accordion-panel.css`

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

`vaadin-accordion-heading.css`

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

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

`vaadin-accordion-panel.css`

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

`vaadin-accordion-heading.css`

```css
[part="content"] {...}
```

### <a id="app-layout"></a>App Layout

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

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

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

The `navbar` now has a defined `min-height`. This change can be reverted with

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

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

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

### <a id="avatar-group"></a>Avatar Group

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

`vaadin-avatar-group.css`

```css
[part="avatar"] {...}
```

`vaadin-avatar-group.css`

```css
::slotted(vaadin-avatar) {...}
```

### <a id="context-menu"></a>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`.

### <a id="combo-box"></a>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>`.

### <a id="crud"></a>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:

`vaadin-crud.css`

```css
[new-button] {...}
/* or */
vaadin-button {...}
```

`vaadin-crud.css`

```css
::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.

### <a id="date-picker-2"></a>Date Picker

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

`vaadin-date-picker-overlay-content.css`

```css
[part="today-button"] {...}
/* and */
[part="cancel-button"] {...}
```

`vaadin-date-picker-overlay-content.css`

```css
::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:

`vaadin-month-calendar.css`

```css
[part="date"] {...}
```

`vaadin-month-calendar.css`

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

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

`vaadin-month-calendar.css`

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

`vaadin-month-calendar.css`

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

### <a id="details"></a>Details

The *summary* (or *header*) part has been refactored into a separate custom element, slotted into the Details component:

`vaadin-details.css`

```css
[part="summary"] {...}
```

`vaadin-details.css`

```css
::slotted(vaadin-details-summary) {...}
```

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

`vaadin-details.css`

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

`vaadin-details-summary.css`

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

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

`vaadin-details.css`

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

`vaadin-details-summary.css`

```css
[part="content"] {...}
```

### <a id="dialog"></a>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.

### <a id="grid"></a>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.

### <a id="login"></a>Login

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

`vaadin-login-form-wrapper.css`

```css
#forgotPasswordButton {...}
/* or */
vaadin-button[theme~="forgot-password"] {...}
/* or */
vaadin-button {...}
```

`vaadin-login-form-wrapper.css`

```css
::slotted([slot="forgot-password"]) {...}
```

### <a id="menu-bar"></a>Menu Bar

The menu-bar buttons (i.e. the top-level menu items) have been moved from shadow DOM to a slot:

`vaadin-menu-bar.css`

```css
[part="menu-bar-button"] {...}
/* or */
vaadin-menu-bar-button {...}
```

`vaadin-menu-bar.css`

```css
::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.

### <a id="message-input"></a>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.css`

```css
vaadin-message-input-text-area {...}
/* and */
vaadin-message-input-button {...}
```

`vaadin-message-input.css`

```css
::slotted(vaadin-text-area) {...}
/* and */
::slotted(vaadin-button) {...}
```

### <a id="message-list"></a>Message List

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

`vaadin-message-list.css`

```css
vaadin-message {...}
```

`vaadin-message-list.css`

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

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

`vaadin-message.css`

```css
[part="avatar"] {...}
/* or */
vaadin-message-avatar {...}
```

`vaadin-message.css`

```css
::slotted(vaadin-avatar) {...}
```

### <a id="multi-select-combo-box"></a>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.css`

```css
vaadin-multi-select-combo-box-chip {...}
[part~="chip"] {...}
[part~="overflow"] {...}
[part~="overflow"][part~="overflow-one"] {...}
```

`vaadin-multi-select-combo-box.css`

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

### <a id="notification"></a>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.

### <a id="upload"></a>Upload

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

`vaadin-upload.css`

```css
[part="file-list"] {...}
```

`vaadin-upload.css`

```css
::slotted(vaadin-upload-file-list) {...}
```

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

`vaadin-upload.css`

```css
[part="upload-button"] {...}
/* or */
#uploadButton {...}
/* or */
vaadin-button {...}
```

`vaadin-upload.css`

```css
::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:

`vaadin-upload.css`

```css
#dropLabel {...}
/* and */
[part="drop-label-icon"] {...}
```

`vaadin-upload.css`

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

## <a id="field-validation-changes"></a>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.

### <a id="binder-considers-component-constraints"></a>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](https://github.com/vaadin/flow-components/issues/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](https://github.com/vaadin/flow/issues/17178)).

### <a id="blur-triggers-server-side-validation"></a>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](https://github.com/vaadin/web-components/issues/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](https://github.com/vaadin/flow-components/issues/4390)).

### <a id="bad-input-invalidates-component"></a>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.

### <a id="manual-validation-mode"></a>Manual Validation Mode (since V24.2)

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`:

```java
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);
    }
});
```

## <a id="removed-deprecations"></a>Removed Deprecations

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

- [Remove deprecated API and tools in Flow 24.0](https://github.com/vaadin/flow/issues/15665)

## <a id="side-navigation-replaces-appnav-and-appnavitem"></a>Side Navigation Replaces AppNav and AppNavItem

Some starter projects previously used the `AppNav` and `AppNavItem` components, which relied on the Vaadin Component Factory components, vcf-nav and vcf-nav-item. These Component Factory components are no longer supported and you should replace `AppNav` and `AppNavItem` with their successor, [Side Navigation](https://vaadin.com/docs/v24/components/side-nav.md).
