# Official instructions, plus my details
The Vaadin company blog page [*Vaadin 24 pre-release available for Spring Boot 3.0*](https://vaadin.com/blog/vaadin-24-pre-release-available-for-spring-boot-3.0), provides some instructions for migrating from Vaadin 23 to Vaadin 24. I can supplement that page with more details.
## First, migrate to latest Vaadin 23
That page first instructs us to migrate to the latest version of Vaadin 23, currently 23.3.1. We see you did that your existing Maven [POM][1] file.
Just be sure to run your app to verify Vaadin 23 is working well before trying Vaadin 24.
## Java version
That page says to next ensure that your project can run on Java 17. We see you did that too in your existing POM, running on Java 19 (the current Java release).
## Vaadin version
Next we check [this GitHub page](https://github.com/vaadin/platform/releases) to capture the current latest version of Vaadin 24. Currently that would be `24.0.0.alpha6`. So we paste that into the POM.
## Servlet spec
Next, look for the `javax.servlet-api` entry in your POM.
Vaadin 23 and earlier was designed for [Java Servlet Specification][2] version 3.1, and is compatible with Java Servlet Spec 4.0.1.
As we jump to Jakarta 10, the name of the spec changes to [*Jakarta Servlet*][3]. And the [Servlet Spec is version 6][4] in Jakarta EE 10. We can verify with a Maven repository such as [this one](https://mvnrepository.com/artifact/jakarta.servlet/jakarta.servlet-api) that the latest subversion of Servlet 6 is `6.0.0`.
So we change this part of the POM:
```xml
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
```
… to:
```xml
<!-- https://mvnrepository.com/artifact/jakarta.servlet/jakarta.servlet-api -->
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
<version>6.0.0</version>
<scope>provided</scope>
</dependency>
```
## Jetty
Your Vaadin 23 app is bundled with an embedded version of Jetty. This enables you to run your Vaadin web app using Jetty as the deployment web app server *within* your [IDE][5]. This arrangement is convenient, relieving you of the chore of configuring an external web app server.
You may nevertheless choose to use an externally web app server running in its own process, such as Tomcat, a separately-installed Jetty, Glassfish, WildFly, Payara, OpenLiberty, or any of several other such products. But if you wish to use the embedded Jetty within your IDE, read on.
Your existing POM is set to use `jetty-maven-plugin` version 10.0.x. That corresponds to Jetty 10. That version of [Jetty][6] supports Servlet 4.
But we are moving to Servlet 6. That requires a different version of Jetty, Jetty 12. Unfortunately, Jetty 12 seems to be still in development, [hosted on GitHub][7]. But no release yet. If curious, see this 2022-04 talk on YouTube, [*Jakarta Tech Talk - Implementing Servlet 6.0 in Jetty*](https://youtu.be/2D3N5ztgKvU).
So we have a predicament, if we want to run Jetty embedded conveniently within our IDE. Technically Vaadin 24 is built for Jakarta 10, which implies Servlet 6. But we only have Jetty 11 available to us, which supports Servlet 5 rather than Servlet 6. But perhaps we might be lucky to find that Vaadin 24, because it has no significant feature changes from Vaadin 23, may not actually be using any new features available only in Servlet 6, nor using any old features deleted from Servlet 6. If so, we may get away with using version of `11.0.13` of `jetty-maven-plugin` to utilize Jetty 5. And … yes, I found success! Using embedded Jetty 11 with Vaadin 24 is working, at least for now.
## Voilà
And that is all we need to do to migrate your Vaadin 23 app to Vaadin 24 (alpha 6).
You can use the Maven Jetty plugin command `jetty:run` to launch your web app just as you do with Vaadin 23.
Here is my resulting Maven POM file.
```xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns = "http://maven.apache.org/POM/4.0.0"
xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation = "http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>project-base</artifactId>
<name>Project base for Vaadin</name>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<properties>
<maven.compiler.source>19</maven.compiler.source>
<maven.compiler.target>19</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<failOnMissingWebXml>false</failOnMissingWebXml>
<vaadin.version>24.0.0.alpha6</vaadin.version>
<drivers.downloader.phase>pre-integration-test</drivers.downloader.phase>
</properties>
<repositories>
<!-- The order of definitions matters. Explicitly defining central here to make sure it has the highest priority. -->
<repository>
<id>central</id>
<url>https://repo.maven.apache.org/maven2</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
<repository>
<id>vaadin-prereleases</id>
<url>
https://maven.vaadin.com/vaadin-prereleases/
</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
<!-- Repository used by many Vaadin add-ons -->
<repository>
<id>Vaadin Directory</id>
<url>https://maven.vaadin.com/vaadin-addons</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
<pluginRepositories>
<!-- The order of definitions matters. Explicitly defining central here to make sure it has the highest priority. -->
<pluginRepository>
<id>central</id>
<url>https://repo.maven.apache.org/maven2</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</pluginRepository>
<pluginRepository>
<id>vaadin-prereleases</id>
<url>
https://maven.vaadin.com/vaadin-prereleases/
</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</pluginRepository>
</pluginRepositories>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.vaadin</groupId>
<artifactId>vaadin-bom</artifactId>
<type>pom</type>
<scope>import</scope>
<version>${vaadin.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>com.vaadin</groupId>
<!-- Replace artifactId with vaadin-core to use only free components -->
<artifactId>vaadin</artifactId>
</dependency>
<!-- Added to provide logging output as Vaadin uses -->
<!-- the unbound SLF4J no-operation (NOP) logger implementation -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/jakarta.servlet/jakarta.servlet-api -->
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
<version>6.0.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.vaadin</groupId>
<artifactId>vaadin-testbench</artifactId>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/io.github.bonigarcia/webdrivermanager -->
<dependency>
<groupId>io.github.bonigarcia</groupId>
<artifactId>webdrivermanager</artifactId>
<version>5.3.1</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<defaultGoal>jetty:run</defaultGoal>
<plugins>
<!-- Define newer versions of Java compiler and war plugin to
better support the latest JDK versions. -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.10.1</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>3.3.2</version>
</plugin>
<!-- Jetty plugin for easy testing without a separate server -->
<plugin>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-maven-plugin</artifactId>
<version>11.0.13</version>
<configuration>
<!--
Configures automatic reload of Jetty server
(with 2 second timeout) when new classes are compiled
(e.g. by IDEs).
Should be disabled when using a proper live reload system,
such as JRebel.
If using IntelliJ IDEA with autocompilation, this
might cause lots of unnecessary compilations in the
background. Consider using "0" and trigger restart manually
by hitting enter.
-->
<scan>2</scan>
<!-- Use war output directory to get the webpack files -->
<!--<webAppConfig>-->
<!-- <allowDuplicateFragmentNames>true</allowDuplicateFragmentNames>-->
<!--</webAppConfig>-->
</configuration>
</plugin>
<!--
Take care of synchronizing java dependencies and imports in
package.json and main.js files.
It also creates webpack.config.js if not exists yet.
-->
<plugin>
<groupId>com.vaadin</groupId>
<artifactId>vaadin-maven-plugin</artifactId>
<version>${vaadin.version}</version>
<executions>
<execution>
<goals>
<goal>prepare-frontend</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
<profiles>
<profile>
<!-- Production mode is activated using -Pproduction -->
<id>production</id>
<build>
<plugins>
<plugin>
<groupId>com.vaadin</groupId>
<artifactId>vaadin-maven-plugin</artifactId>
<version>${vaadin.version}</version>
<executions>
<execution>
<goals>
<goal>build-frontend</goal>
</goals>
<phase>compile</phase>
</execution>
</executions>
<configuration>
<productionMode>true</productionMode>
</configuration>
</plugin>
</plugins>
</build>
</profile>
<profile>
<id>it</id>
<build>
<plugins>
<plugin>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-maven-plugin</artifactId>
<configuration>
<!--<scanIntervalSeconds>0</scanIntervalSeconds>-->
<stopPort>8081</stopPort>
<stopWait>5</stopWait>
<stopKey>${project.artifactId}</stopKey>
</configuration>
<executions>
<execution>
<id>start-jetty</id>
<phase>pre-integration-test</phase>
<goals>
<goal>start</goal>
</goals>
</execution>
<execution>
<id>stop-jetty</id>
<phase>post-integration-test</phase>
<goals>
<goal>stop</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- Runs the integration tests (*IT) after the server is started -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-failsafe-plugin</artifactId>
<version>3.0.0-M7</version>
<executions>
<execution>
<goals>
<goal>integration-test</goal>
<goal>verify</goal>
</goals>
</execution>
</executions>
<configuration>
<trimStackTrace>false</trimStackTrace>
<enableAssertions>true</enableAssertions>
</configuration>
</plugin>
</plugins>
</build>
</profile>
</profiles>
</project>
```
[1]: https://en.wikipedia.org/wiki/Apache_Maven#Project_Object_Model
[2]: https://en.wikipedia.org/wiki/Jakarta_Servlet
[3]: https://jakarta.ee/specifications/servlet/
[4]: https://jakarta.ee/specifications/servlet/6.0/
[5]: https://en.wikipedia.org/wiki/Integrated_development_environment
[6]: https://en.wikipedia.org/wiki/Jetty_(web_server)
[7]: https://github.com/eclipse/jetty.project/tree/jetty-12.0.x
**Disclaimer:** Probably this would be better suited as a comment but it does not fit the space.
----
**1)** You can use the stable [Vaadin 8.1.0][1] and [vaadin-spring-boot-starter 2.0.1][2]. The Vaadin module versions are managed by the `vaadin-bom` (`${vaadin.version}` is a property in my pom and has a value of 8.1.0):
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.vaadin</groupId>
<artifactId>vaadin-bom</artifactId>
<version>${vaadin.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
After this, you won't need to change their versions each time you change your Vaadin version (unless you really need to use a specific version):
<dependency>
<groupId>com.vaadin</groupId>
<artifactId>vaadin-spring-boot-starter</artifactId>
</dependency>
----
**2)** You should use the same `vaadin-maven-plugin` as your Vaadin version to avoid widgetset version mismatches/issues. You're using Vaadin 8.1.0 but the plugin version is 8.0.6. You can use `<version>${vaadin.version}</version>` to sync them.
----
**3)** As per the [docs][3], vaadin-client-compiler is used only at compile time and should not be deployed with your application:
>**vaadin-client-compiler-8.x.x.jar**<br/>
> The Vaadin Client Compiler is a Java-to-JavaScript compiler that allows building client-side modules, such as the Client-Side Engine (widget set) required for server-side applications. The compiler is needed, for example, for compiling add-on components to the application widget set, as described in "Using Vaadin Add-ons".
>
>For detailed information regarding the compiler, see "Compiling a Client-Side Module". **Note that you should not deploy this library with a web application.**
You can add `<scope>provided</scope>` to the dependency and that should probably fix your tomcat issue.
**Important edit:** I've checked the logs and it looks like you don't even need it in your classpath for your widgetset. The `vaadin-maven-plugin` uses it to compile your widgetset _behind the curtain_:
<pre>
[INFO] --- vaadin-maven-plugin:8.1.0:compile (default) @ test ---
[INFO] Using com.vaadin:vaadin-client-compiler version 8.1.0
[INFO] Compiling module MyCustomWidgetSet
[INFO] Computing all possible rebind results for 'com.vaadin.client.metadata.ConnectorBundleLoader'
...
</pre>
After removing it, everything went as expected. Not entirely sure, but it's possible that it's required only for [certain add-ons or the `vaadin-maven-plugin`][4]. I didn't need to write any so far, so perhaps someone that has actually required it could provide more insight.
----
**4)** You can also remove `vaadin-client-compiled` as per the same [docs][3] suggestion.
[1]: https://vaadin.com/releases?version=release/8.1/8.1.0
[2]: https://mvnrepository.com/artifact/com.vaadin/vaadin-spring-boot-starter
[3]: https://vaadin.com/docs/-/part/framework/getting-started/getting-started-libraries.html
[4]: https://mvnrepository.com/artifact/com.vaadin/vaadin-client-compiler/usages