Documentation versions (currently viewingVaadin 24)

Production Build

Create an optimized build of your applications for production deployment.

To create a production build, run the following command:

mvn clean package -Pproduction

This command builds a JAR or WAR file, with all the dependencies and bundled front-end resources, ready to be deployed. You can find the file in the target folder after the build is completed.

If no add-ons or front-end files are used in the application, the previous command uses a pre-compiled production bundle to eliminate the need to run front-end tools like npm and Vite. However, front-end tools are still used to generate an application-specific bundle in the following situations:

  • An npm/pnpm package is added with @NpmPackage or directly into package.json;

  • CSS or JavaScript is added with @CssImport, @JsModule or @JavaScript;

  • Vaadin add-on with front-end customizations is added;

  • Custom theme packaged as a JAR dependency is included, if it defines any assets to be added to the project;

  • Exported web component is added; or

  • @LoadDependenciesOnStartup is defined for AppShellConfigurator.

Production builds that use npm and Vite are slower to generate than builds that use a pre-compiled bundle. However, builds that use npm and Vite are more optimized in the sense that they only include the front-end resources that are used in the application.

If needed, you can force the creation of an optimized production bundle by executing the following from the command-line:

mvn clean package -Pproduction

Enabling Production Builds

The production build command works out-of-the-box for Vaadin starter projects. For example, projects that are generated with The starter projects come with the necessary Maven configuration. If you’ve manually created your project’s pom.xml file, add the following Maven profile to enable production builds:


		.. configuration depending on environment ..

                        <!-- To always force a production build set this configuration to 'true' -->
                        <!-- To possibly use the pre-generated bundle remove or set to 'false' -->
                .. more plugins ..
		 .. more configuration ..

The content of the profile depends on the environment in which your application is running, but all variations call the Maven goal vaadin:build-frontend. The Maven goal vaadin:prepare-frontend is also required, but that’s often declared already in the development build. Once the Maven profile is added, you can call the production build command.

If you don’t have the production Maven profile in your pom.xml file, get a project base: for Spring Boot projects, get it from; for other stacks (e.g., Jakarta EE or plain Java) from Then copy the production profile from the downloaded pom.xml file.

Having the production build as a separate Maven profile is recommended to avoid unexpected problems due to production settings during development.

Building for 64-bit
If your operating system is 64-bit, make sure to use a 64-bit JDK installation, as well.

Excluding the Development Server Module

The Vite server integration and live reload features, which are available only in development builds, are contained in the com.vaadin:vaadin-dev-server module. You should exclude this module when building a production application. You can do this by adding the following dependency exclusion to the <dependencies> section in the production profile:


        <!-- above production build configuration -->


This results in less code and fewer dependency libraries being bundled in the production application.

Transpilation & Bundling

Transpilation in Vaadin means converting all TypeScript files to JavaScript (ES2021) supported by modern browsers.

During the build, minimization is done to make the files smaller. When minifying code, it’s often obfuscated, which makes it harder to read. Hence, this isn’t done for development builds.

Bundling is an optimization where multiple files are merged into a single collection, so that the browser doesn’t need to request so many files from the server. This makes the application load faster.

Plugin Goals & Goal Parameters


This goal validates whether the node and npm tools are installed and not too old (i.e., not earlier than node version 16.14 and not older than npm version 8.3). It installs them in the .vaadin folder in the user’s home directory if they’re missing. If they’re already installed globally but too old, an error message is generated suggesting that you install newer versions. Node.js is needed to run npm to install front-end dependencies and Vite, which bundles the front-end files served to the client.

Additionally, it reviews all resources used by the application and copies them under the node_modules folder, so they’re available when vite builds the frontend. It also creates or updates the package.json, vite.config.ts and vite.generated.ts files.

This plugin has several goal parameters:

  • includes (default: **/*.js,**/*.css): Comma-separated wildcards for files and directories that should be copied. The default is only .js and .css files.

  • npmFolder (default: ${project.basedir}): The folder where the package.json file is located. The default is the project root folder.

  • generatedFolder (default: ${}/frontend/): The folder where Flow puts generated files that are used by Vite.

  • require.home.node (default: false): If set to true, always prefer Node.js is automatically downloaded and installed into the .vaadin directory in the user’s home directory.


This goal builds the front-end bundle. This is a complex process involving several steps:

  • Update package.json with all the @NpmPackage annotation values found in the classpath and install these dependencies.

  • Update the JavaScript files containing code for importing everything used in the application. These files are generated in the target/frontend folder, and are used as the entry point of the application.

  • Create vite.config.ts if not found, or update it if some project parameters have changed.

  • Generate JavaScript bundles and chunks and compile TypeScript to JavaScript using vite server. The target folder for WAR packaging is target/${artifactId}-${version}/build. For JAR packaging, it’s target/classes/META-INF/resources/build.

This plugin has several goal parameters:

npmFolder (default: ${project.basedir}

The folder where the package.json file is located. The default is the project root folder.

generatedFolder (default: ${}/frontend/)

The folder where Flow puts generated files used by Vite.

frontendDirectory (default: ${project.basedir}/frontend)

The directory with the project’s front-end source files.

generateBundle (default: true)

Whether to generate a bundle from the project front-end sources.

runNpmInstall (default: true)

Whether to run pnpm install (or npm install, depending on pnpmEnable parameter value) after updating dependencies.

generateEmbeddableWebComponents (default: true)

Whether to generate embedded web components from WebComponentExporter inheritors.

optimizeBundle (default: true)

Whether to include only front-end resources used from application entry points (the default) or to include all resources found on the classpath. This should normally be left to the default, but a value of false can be useful for faster production builds or debugging discrepancies between development and production builds.

pnpmEnable (default: false)

Whether to use the pnpm or npm tool to handle front-end resources. The default is npm.

useGlobalPnpm (default: false)

Whether to use a globally installed pnpm tool instead of the default supported version of pnpm.

forceProductionBuild (default: false)

Whether to generate a production bundle even if an existing pre-generated bundle could be used.


This goal removes files that may cause inconsistencies when changing versions. It’s suggested not to add the goal as a default to pom.xml and instead use it with mvn vaadin:clean-frontend when necessary.

Executing the clean-frontend goal removes the package lock file, the generated frontend folder (by default frontend/generated), and the node_modules folder, which might need to be deleted manually.

This goal also cleans all dependencies that are framework-managed, and any dependencies that target the build folder from the package.json file.

The clean-frontend goal supports the same parameters as prepare-frontend.


This goal is synonymous with the clean-frontend goal.

Bundle Component Loading Optimizations

Lazy Loading (On Demand)

A production build scans for Routes and lazy loads the components used in the routes when navigated. By default, only the routes "" and "login" are eager loaded and other route components become lazy loaded.

With the pre-compiled production bundle, all components are eager loaded apart from the heavy components Map, Charts, Spreadsheet and RichTextEditor.

Loading Components
Any components that are loaded using reflection should be named on the Route class using @Uses so that they’re collected.
public class MyView extends Div {
    public MyView() {
        try {
            Class<? extends Button> buttonClass = Class.forName(
            Button button = buttonClass.getDeclaredConstructor().newInstance();
        } catch (ClassNotFoundException e) {
            // handle exception

Eager Loading

To configure which views should be eager loaded, use the annotation @LoadDependenciesOnStartup on the AppShellConfiguration class. Only defining LoadDependenciesOnStartup makes all routes eager loaded.

public class Configuration implements AppShellConfigurator {

To configure specific routes to be eager loaded, add the route class to the value array like this:

@LoadDependenciesOnStartup({GeneralInfo.class, DataSearch.class})
public class Configuration implements AppShellConfigurator {

This makes components, scripts, and CSS used in GeneralInfo and DataSearch load immediately on bootstrap and any other components used in other views as they’re needed.