Production Build
To create a production build, run the following from the command-line:
mvn clean package -Pproduction
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 throughpackage.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 forAppShellConfigurator
.
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:
mvn clean package -Pproduction -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. Before Vaadin 24.3, output from an optimized production bundle is not used as a pre-compiled bundle for later production builds. Only the pre-compiled production bundle from platform is used, if possible. |
By default, Vaadin uses npm install
to install frontend packages. This can be changed to npm ci
by setting the following configuration parameter:
-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.
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 Maven profile to enable production builds:
<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>
<phase>compile</phase>
</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>
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 https://start.vaadin.com
; for other stacks (e.g., Jakarta EE or plain Java) from https://vaadin.com/hello-world-starters
. 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.
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.
|
Excluding 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:
<profiles>
<profile>
<id>production</id>
<!-- above production build configuration -->
<dependencies>
<dependency>
<groupId>com.vaadin</groupId>
<artifactId>vaadin</artifactId>
<exclusions>
<exclusion>
<groupId>com.vaadin</groupId>
<artifactId>vaadin-dev</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
</profile>
</profiles>
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), which is 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 more difficult to read. Hence, this isn’t done for development builds.
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.
Plugin Goals & Goal Parameters
This section contains a list of plugin goals and their parameters.
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 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 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:
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. - generatedTsFolder (
${project.basedir}/src/main/frontend/generated
) -
The folder where Vaadin puts generated files. If not given, will be
generated
folder underfrontendDirectory
parameter. require.home.node
(default:false
)-
If set to
true
, always preferNode.js
is automatically downloaded and installed into the.vaadin
sub-directory, in the user’s home directory.
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 forWAR
packaging istarget/${artifactId}-${version}/build
. ForJAR
packaging, it’starget/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 underfrontendDirectory
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
— ornpm 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
ornpm
tool to handle frontend resources. The default isnpm
. useGlobalPnpm
(default:false
)-
Whether to use a globally installed
pnpm
tool instead of the default supported version ofpnpm
. 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
). Fallbacks tovaadin-router
, excludes all React dependencies and addsLit
dependencies, if set tofalse
.
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
.
Bundle Component Loading Optimizations
You can do a few things to optimize the loading of a bundle component. They’re described in this section.
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.
|
@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
}
}
}
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.
@LoadDependenciesOnStartup
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.
B88A9480-7687-4B97-B202-E39731DDF164