Vaadin 14 - The missing guide
The Vaadin documentation is a good source of initial info for Vaadin, but unfortunately in certain cases it simply doesn’t go deep enough. This is especially true with respect of the toolchain Vaadin uses, and the files it relies on. Therefore, this blog post.
Basic Vaadin concepts
All Vaadin components are generally web components, following the Web Component specification.
Web Component API is supported by all ever-green browsers (Firefox, Chrome, Safari, MS Edge).
Note: Ever-green means that the browser endlessly updates itself and you generally use the newest version.
Web Component API can be emulated, by the means of the Polyfill library - that’s how IE 11 is able to “support” Web Components even though IE 11 offers no Web Component support natively. However, Polyfills emulation is very slow and leads to 5-10 times worse performance (for example documented in Vaadin dropped IE11 support in Vaadin 15+; also see Vaadin Release Notes, section “Known Issues and Limitations” / “Performance” - very well hidden Vaadin!!)
Vaadin controls the web components from server side, by setting DOM HTML attributes, Polymer properties (kinda deprecated since Polymer will go away) and Lit Properties (Vaadin 15+); Vaadin is also able to listen on DOM events and pass them through to the server-side. The Vaadin part responsible for this is called Vaadin Flow.
Read Vaadin Overview for more details.
Note: You can still use non-webcomponent libraries: you simply create a
<div>
client-side, you hook the javascript library to that div and then you can control the<div>
from the server-side.
Basic JavaScript concepts
- node.js - a virtual machine able to run JavaScript code, akin to JVM
- npm - a package manager akin to Maven
- pnpm - a package manager akin to Maven; same as
npm
but much faster since it’s able to cache downloaded packages locally. - Bower - a package manager akin to Maven, now deprecated and superseded by npm
For best results, use pnpm
. In the text below, npm
will be used as a reference to both
npm and pnpm.
The Compatibility/bower mode versus the npm mode
Vaadin is able to run in two modes: so-called compatibility or bower mode, and the npm mode. Vaadin 13 and lower only supported bower mode; Vaadin 14 supports both modes; Vaadin 15+ will only support npm mode.
In compatibility mode, Vaadin Servlet will use the old way of using
JavaScript modules packaged as webjars (jars deployed in Maven Central, containing client-side JavaScript code).
The packaging manager is Bower. Generally the webjars
site is used, to create webjar out of a bower package. What the webjars page does
is that it parses bower descriptor, creates a Maven pom.xml
out of it (so that
transitive dependencies are “working”) and that’s it: from your server-side perspective
you can forget about Bower entirely and just use Maven to manage the dependencies.
Well, not entirely: Bower transitive versioning resolution algorithm works
a bit differently than the Maven one (especially for version ranges such as 1.+);
Vaadin had to workaround these issues by fixing versions of all webjars in the
vaadin-bom
but this approach is error-prone and maintenance-heavy. That’s why
bower is deprecated in favor of npm/pnpm.
Bower mode is deprecated and will go away in future Vaadin versions, do not use it for new projects - always use the npm mode.
new npm mode
In npm mode, Vaadin does not use webjars at all; instead it relies on JavaScript programs to do all the work:
- It uses the
npm
/pnpm
package manager to gather all packages Vaadin+your app needs, into thenode_modules
folder. - webpack is then used to compress all modules and custom js files into one huge javascript file, both in development mode and in production mode.
- (In Vaadin production mode): the javascript file is then stored into the WAR file
into the
WEB-INF/classes/META-INF/VAADIN/build/
folder and served by VaadinServlet from there.
Note: In npm mode, Vaadin does not use webjars at all. However in Vaadin 14 webjars are transitive dependencies of the
vaadin
andvaadin-core
Maven artifacts. While there is no harm in having webjars on classpath (Vaadin will simply ignore them), it’s best to exclude them in order to decrease the result WAR file size. See example build.gradle.kts on how to do that.
npm configures itself via the package.json
and package-lock.json
files,
and produces the node_modules
folder.
The package.json
and package-lock.json
files are generated by Vaadin, but you
are free to modify those files to include additional packages if need be. However
there’s a better way to do it: by using the @NpmPackage
Java annotation you declare
that you need this and this npm package for your server-side component. Vaadin plugin
then performs classpath scanning, gathers all @NpmPackage
annotations and produces
appropriate package.json
/package-lock.json
files.
Webpack basically takes all npm modules and the content of your frontend/
folder,
and produces one huge javascript file. Webpack is configured via two files:
webpack.config.js
and webpack.generated.js
. You can add your custom config into the
webpack.config.js
file, however I never needed to do that so far. Both files
are generated by Vaadin.
Development vs production mode
In development mode, Vaadin doesn’t bundle the javascript files into the WAR file since
that would prevent them from being hot-redeployed when modified. Instead,
Vaadin Servlet will run webpack as a child process; webpack will then monitor
frontend/
for changes and is able to communicate with Vaadin Servlet internally
so that Vaadin Servlet can correctly serve up-to-date JavaScript code. That’s why any changes
made to the frontend/
folder are immediately visible when you refresh the page.
In production mode however, Vaadin Servlet doesn’t launch webpack (since that would
require having node.js and npm on your production machine); instead it relies on Vaadin Plugin
to run webpack, in order to produce the one huge JavaScript file. Vaadin Plugin
then packages the produced JavaScript file onto classpath, into the META-INF/VAADIN/build/
package;
Vaadin Servlet is then able to serve those files to the browser, in order to
initialize the client-side properly.
The META-INF/VAADIN/build/
contents should look like this (the hash is going to differ on every build):
├── vaadin-2-18d67c4ccff7e93b081a.cache.js
├── vaadin-2-18d67c4ccff7e93b081a.cache.js.gz
├── vaadin-3-b0147df339bf18eb7618.cache.js
├── vaadin-3-b0147df339bf18eb7618.cache.js.gz
├── vaadin-4-ee1d2e45569f7eca4292.cache.js
├── vaadin-4-ee1d2e45569f7eca4292.cache.js.gz
├── vaadin-5-5e9292474e82143d0a27.cache.js
├── vaadin-5-5e9292474e82143d0a27.cache.js.gz
├── vaadin-bundle-19a00eae62ad7cddd291.cache.js
├── vaadin-bundle-19a00eae62ad7cddd291.cache.js.gz
├── vaadin-bundle.es5-b1c1a3cc054c62ad7949.cache.js
├── vaadin-bundle.es5-b1c1a3cc054c62ad7949.cache.js.gz
└── webcomponentsjs
├── bundles
│ ├── webcomponents-ce.js
│ ├── webcomponents-ce.js.map
│ ├── webcomponents-sd-ce.js
│ ├── webcomponents-sd-ce.js.map
│ ├── webcomponents-sd-ce-pf.js
│ ├── webcomponents-sd-ce-pf.js.map
│ ├── webcomponents-sd.js
│ └── webcomponents-sd.js.map
├── custom-elements-es5-adapter.js
├── LICENSE.md
├── package.json
├── README.md
├── src
│ └── entrypoints
│ ├── custom-elements-es5-adapter-index.js
│ ├── webcomponents-bundle-index.js
│ ├── webcomponents-ce-index.js
│ ├── webcomponents-sd-ce-index.js
│ ├── webcomponents-sd-ce-pf-index.js
│ └── webcomponents-sd-index.js
├── webcomponents-bundle.js
├── webcomponents-bundle.js.map
└── webcomponents-loader.js
Vaadin Configuration
The most important file is the flow-build-info.json
file: Vaadin Servlet
reads that file and configures itself from it at runtime.
The file resides on the classpath, in the
META-INF/VAADIN/config/
package. The file is produced by Vaadin Plugin.
It looks like this (development mode example):
{
"compatibilityMode": false,
"productionMode": false,
"npmFolder": "/home/mavi/work/my/vok/karibu10-helloworld-application",
"generatedFolder": "/home/mavi/work/my/vok/karibu10-helloworld-application/target/frontend",
"frontendFolder": "/home/mavi/work/my/vok/karibu10-helloworld-application/frontend",
"pnpm.enable": true,
"require.home.node": false
}
The most important settings are compatibilityMode
and productionMode
.
Production mode example:
{
"compatibilityMode": false,
"productionMode": true,
"enableDevServer": false,
"chunks": {
"fallback": {
"jsModules": [
"@vaadin/vaadin-icons/vaadin-icons.js",
"@vaadin/vaadin-grid/src/vaadin-grid-tree-toggle.js",
// etc etc
"frontend://ironListConnector.js"
],
"cssImports": [
]
}
}
}
It’s very important to have this file on classpath. If it’s not, Vaadin Servlet
will then try to auto-configure itself and sometimes wrongly so. For example when launching
WAR in Tomcat from Intellij, the frontendFolder
setting will be “auto-detected”
to Tomcat’s bin/
folder, which will cause webpack to fail later on, because of missing files.
If the file is present multiple times on the classpath,
then Vaadin tries to choose which one is the right one (which one is coming
from the main project). That’s unfortunate since sometimes the algorithm fails and
backfires unexpectedly, choosing the incorrect file
(see Flow issue #8936 for more details).
This usually happens when a Vaadin addon
incorrectly includes the flow-build-info.json
file in the jar file.
That’s a bug in the addon packaging which need to be fixed and the flow-build-info.json
file removed from their jar files.
For the reasons stated above, keep in mind to have the flow-build-info.json
file on the classpath EXACTLY ONCE,
and always coming from your main project.
The project flow-build-info.json
file is generated by Vaadin plugin, in the prepare-frontend
task,
therefore it’s very important to always run that task before launching your app in development mode.
Checking whether Vaadin runs in development or in production mode
The most important setting is the productionMode
in flow-build-info.json
which configures
Vaadin to run in either development or production mode.
If Vaadin runs in the development mode it will log the following to the console/logger:
[http-nio-8080-exec-1] INFO com.vaadin.flow.server.DefaultDeploymentConfiguration -
Vaadin is running in DEBUG MODE.
When deploying application for production, remember to disable debug features. See more from https://vaadin.com/docs/
If Vaadin runs in production mode it will log the following:
[http-nio-8080-exec-1] INFO com.vaadin.flow.server.DefaultDeploymentConfiguration - Vaadin is running in production mode.
What to read next
Read the Vaadin Plugin: under the hood for more info on how these files are prepared.