> Markdown version of [Production Build](https://vaadin.com/docs/next/flow/production/production-build). Section index: [llms.txt](https://vaadin.com/docs/next/flow/llms.txt)

# Production Build

To create a production build, run the following from the command-line:

```terminal
mvn clean package
```

Executing this line builds a `JAR` or `WAR` file, with all of the dependencies and bundled frontend resources, ready to be deployed. You can find the file in the `target` folder after the build is finished.

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

- An `npm` package is added with `@NpmPackage`, or provided directly through `package.json`;

- CSS or JavaScript is added with `@CssImport`, `@JsModule`, or `@JavaScript`;

- Vaadin add-on with frontend 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 frontend resources that are used in the application.

When building a new optimized production bundle, the output is always compressed to the `src/main/bundles/prod.bundle` file inside the project’s root. It’s used as a pre-compiled bundle for later production builds. The file can be added to the Version Control System. This allows other developers to fetch the production bundle and thereby run it.

The actual bundle files go to the build directory (i.e., `target` for Maven, `build` for Gradle) and are used from there so that minor changes don’t cause multiple files to change and be committed to the Version Control System.

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

```terminal
mvn clean package -Dvaadin.force.production.build=true
```

> **Note:** The pre-compiled production bundle is not the same as a pre-compiled frontend bundle for development mode. The development mode pre-compiled bundle has debugging features enabled.

By default, Vaadin uses `npm install` to install frontend packages. This can be changed to `npm ci` by setting the following configuration parameter:

**System Property**

```terminal
-Dvaadin.ci.build=true
```

When set, Vaadin interrupts package installation if it spots any mismatch between package versions in the `package.json` and the lock file. It’ll then recommend re-running `npm install`. This makes production builds in the CI pipelines reproducible, enabling you to identify problems in advance.

## <a id="enabling-production-builds"></a>Enabling Production Builds

The production build command works out-of-the-box for Vaadin starter projects. It’ll work with projects that are generated with `https://start.vaadin.com`. The starter projects come with the necessary Maven configuration.

If you’ve manually created your project’s `pom.xml` file, add the following `vaadin-maven-plugin` to enable production builds when using Spring Boot:

`pom.xml`

```xml
<!--
.. configuration depending on environment ..
 -->

<build>
    <plugins>
        <plugin>
            <groupId>com.vaadin</groupId>
            <artifactId>vaadin-maven-plugin</artifactId>
            <version>${project.version}</version>
            <executions>
                <execution>
                    <goals>
                        <goal>build-frontend</goal>
                    </goals>
                </execution>
            </executions>
            <configuration>
                <!-- To always force an optimized production bundle build set this configuration to 'true' -->
                <!-- To possibly use the pre-compiled production bundle remove or set to 'false' -->
                <forceProductionBuild>true</forceProductionBuild>
                <!-- To have reproducible build by running 'npm ci' -->
                <ciBuild>true</ciBuild>
            </configuration>
        </plugin>
        <!--
        .. more plugins ..
        -->
    </plugins>
</build>
 <!--
 .. more configuration ..
 -->
```

The content depends on the environment in which your application is running, but all variations call the Maven goal `vaadin:build-frontend`. Once the `vaadin:build-frontend` goal is added, you can call the production build command.

If you don’t have the `build-frontend` goal in your `pom.xml` file, get a project base for Spring Boot projects from `https://start.vaadin.com`. Then copy the `vaadin-maven-plugin` plugin from the downloaded `pom.xml` file.

For other applications, e.g., Jakarta EE or plain Java, and for better backwards compatibility, add the following Maven profile to enable production builds and get a project base from `https://vaadin.com/hello-world-starters`:

`pom.xml`

```xml
<profiles>
    <profile>
        <id>production</id>

        <!--
		.. configuration depending on environment ..
		 -->

		<build>
			<plugins>
                <plugin>
                    <groupId>com.vaadin</groupId>
                    <artifactId>vaadin-maven-plugin</artifactId>
                    <version>${project.version}</version>
                    <executions>
                        <execution>
                            <goals>
                                <goal>build-frontend</goal>
                            </goals>
                        </execution>
                    </executions>
                    <configuration>
                        <!-- To always force an optimized production bundle build set this configuration to 'true' -->
                        <!-- To possibly use the pre-compiled production bundle remove or set to 'false' -->
                        <forceProductionBuild>true</forceProductionBuild>
                        <!-- To have reproducible build by running 'npm ci' -->
                        <ciBuild>true</ciBuild>
                    </configuration>
                </plugin>
                <!--
                .. more plugins ..
                -->
            </plugins>
        </build>
		 <!--
		 .. more configuration ..
		 -->
    </profile>
</profiles>
```

> **Warning: Caution when defining vaadin-maven-plugin**
>
> To ensure that the production build packages all resources correctly, make sure the `vaadin-maven-plugin` is executed after the `maven-compiler-plugin`. This can be achieved either configuring the execution goal of `vaadin-maven-plugin` to run after the goal targeted by `maven-compiler-plugin`, or if both target the same goal, placing the `vaadin-maven-plugin` definition after the `maven-compiler-plugin` in your `pom.xml`. The same applies for `kotlin-maven-plugin` or any other plugin that produces Vaadin related Java classes.

> **Note: Building for 64-bit**
>
> If you’re using a 64-bit operating system, be sure to use a 64-bit JDK installation, as well.

## <a id="excluding-development-server-module"></a>Excluding Development Server Module

The Vite server integration and live reload features — available only in development builds — are provided by the `com.vaadin:vaadin-dev-server` module, which is included in the `vaadin-dev` module.

When you use `vaadin-dev` in your Maven configuration to run in development mode — within the same Maven profile or project dependencies — you should exclude this module from the production package.

You can do this by declaring the `vaadin-dev` dependency `optional` in your project `<dependencies>`. Maven automatically excludes optional dependencies from the final production package:

`pom.xml`

```xml
<dependencies>
    ...
    <dependency>
        <groupId>com.vaadin</groupId>
        <artifactId>vaadin-dev</artifactId>
        <optional>true</optional>
    </dependency>
    ...
</dependencies>
```

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

## <a id="transpilation-bundling"></a>Transpilation & Bundling

Transpilation in Vaadin means converting all TypeScript files to JavaScript. The JavaScript and CSS are compiled for the browsers that Vaadin supports — see [Browser Compatibility](https://vaadin.com/docs/next/compatibility.md#browser-compatibility).

During the build, minimization is done to make the files smaller. When minifying code, it’s often obfuscated, which makes it more difficult to read. Hence, this isn’t done for development builds. To keep client-side stack traces readable in a production build, generate source maps for the bundle — see [Source Maps in Production](https://vaadin.com/docs/next/flow/production/source-maps.md).

Bundling is an optimization in which multiple files are merged into a single collection. It’s done to reduce the number of files the browser has to request from the server. The result is that the application loads faster.

## <a id="pre-compressed-static-resources"></a>Pre-Compressed Static Resources (since V25.3)

Vaadin has always served a pre-compressed `.br` or `.gz` sibling of a static resource when one exists, and Vite writes those siblings for the assets that go into the frontend bundle. Static resources served straight from `META-INF/resources`, however — a stylesheet referenced with `@StyleSheet`, a JavaScript file, an SVG — never enter that bundle, and so had no compressed variant to serve. A production build now compresses them too, writing a [Brotli](https://github.com/google/brotli) (`.br`) and a gzip (`.gz`) sibling next to each compressible file. The server then sends whichever variant the browser accepts, and the uncompressed file to a browser that accepts neither.

Files with these extensions are compressed, provided they’re at least 1 KB — below that, the compressed variant tends to be no smaller than the original: `.css`, `.js`, `.mjs`, `.cjs`, `.json`, `.map`, `.svg`, `.html`, `.htm`, `.xml`, and `.txt`.

Brotli compression needs Node, which the JDK can’t provide. Node is used only when it’s already available, so it’s never downloaded merely to compress resources; without it, the build writes the gzip variants only. Compression is a best-effort optimization: a failure is logged as a warning and never fails the build, since the uncompressed resource is always there to serve.

Serving the Brotli variants is controlled by the `brotli` [configuration property](https://vaadin.com/docs/next/flow/configuration/properties.md). Set it to `false` to stop serving them; the gzip variants are used regardless.

## <a id="plugin-goals-goal-parameters"></a>Plugin Goals & Goal Parameters

This section contains a list of plugin goals and their parameters.

### <a id="prepare-frontend"></a>`prepare-frontend`

This goal validates whether the `node` and `npm` tools are installed and aren’t too old (i.e., not earlier than `node` version `24`, and not older than `npm` version `11`). 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 frontend dependencies and Vite, which bundles the frontend files served to the client.

Additionally, this goal reviews all resources used by the application. It copies them into 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:

- `npmFolder` (default: `${project.basedir}`)

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

- **generatedTsFolder** (`${project.basedir}/src/main/frontend/generated`)

  The folder where Vaadin puts generated files. If not given, will be `generated` folder under `frontendDirectory` parameter.

- `require.home.node` (default: `false`)

  If set to `true`, always prefer `Node.js` is automatically downloaded and installed into the `.vaadin` sub-directory, in the user’s home directory.

- `frontendExtraFileExtensions` (default: `null`)

  Parameter for adding extensions of files to be stored in the `stats.json` of a generated bundle. For instance, if the project uses `.scss` files they are not automatically taken into account in the bundle hashes.

### <a id="build-frontend"></a>`build-frontend`

This goal builds the frontend 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.

- **generatedTsFolder** (`${project.basedir}/src/main/frontend/generated`)

  The folder where Vaadin puts generated files. If not given, will be `generated` folder under `frontendDirectory` parameter.

- `frontendDirectory` (default: `${project.basedir}/src/main/frontend`)

  The directory with the project’s frontend source files. The legacy location `"${project.basedir}/frontend"` is used if the default location doesn’t exist.

- `generateBundle` (default: `true`)

  Whether to generate a bundle from the project frontend 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 frontend 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 frontend 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.

- `reactEnable` (default: `true`)

  Whether to use React Router, add React core dependencies, React integration helpers and Vaadin’s provided React components (`@vaadin/react-components`). Setting this to `false` (deprecated since V25.0) falls back to the deprecated Vaadin Router, excludes all React dependencies and adds Lit dependencies; that option is removed in Vaadin 26. See [Vaadin Router Deprecation](https://vaadin.com/docs/next/upgrading.md#vaadin-router-removal) for more information.

- `frontendExtraFileExtensions` (default: `null`)

  Parameter for adding extensions of files to be stored in the \[filename]stats.json of a generated bundle. For instance, if the project uses `.scss` files they are not automatically taken into account in the bundle hashes.

### <a id="clean-frontend"></a>`clean-frontend`

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`. Instead, use it with `mvn vaadin:clean-frontend` when necessary.

Executing the `clean-frontend` goal removes the package lock file, and the generated frontend folder — by default `frontend/generated`. It also removes 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`.

Vaadin clears the generated frontend files after building a project for production, if they didn’t exist before the build. See `vaadin.clean.build.frontend.files` and `cleanFrontendFiles` in [Vaadin Plugin Properties](https://vaadin.com/docs/next/flow/configuration/properties.md#vaadin-plugin-properties) for more information.

### <a id="dance"></a>`dance`

This goal is synonymous with the `clean-frontend` goal. See its description above.

## <a id="bundle-component-loading-optimizations"></a>Bundle Component Loading Optimizations

You can do a few things to optimize the loading of a bundle component. They’re described in this section.

### <a id="lazy-loading-on-demand"></a>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`.

> **Note: Loading Components**
>
> Any components that are loaded using reflection should be named on the `Route` class using `@Uses` so that they’re collected.

Uses Example

```java
@Route("my-view")
@Uses(Button.class)
public class MyView extends Div {
    public MyView() {
        try {
            Class<? extends Button> buttonClass = Class.forName(
                    "com.vaadin.flow.component.button.Button");
            Button button = buttonClass.getDeclaredConstructor().newInstance();
            add(button);
        } catch (ClassNotFoundException e) {
            // handle exception
        }
    }
}
```

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

```java
@LoadDependenciesOnStartup
public class Configuration implements AppShellConfigurator {
}
```

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

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

`B88A9480-7687-4B97-B202-E39731DDF164`
