A small library that lets you embed Vaadin Flow in a single executable JAR — spun up against embedded Jetty with a minimal API. No Spring, no application server, no XML. Just a couple of static method calls.
Why this exists
Vaadin’s default packaging path leans on Spring Boot or a servlet container
running a WAR. For tiny tools, internal dashboards, or one-shot utilities
that’s overkill — you want a java -jar foo.jar and be done.
The catch: embedded Jetty’s annotation scanner only walks JARs and
WEB-INF/classes. When the app runs from an IDE or mvn exec:java, your
@Route classes live in plain classpath directories that Jetty’s scanner
ignores. Vaadin’s RouteRegistryInitializer then sees zero routes and your
“Hello World” 404s.
Nano Vaadin Jetty solves that without forcing you into framework-land.
The whole API in three calls
import com.svenruppert.vaadin.nano.CoreUIServiceJava;
// 1. Plain start — caller wires routes elsewhere
Server server = CoreUIServiceJava
.startServer("127.0.0.1", 8080)
.getOrThrow();
// 2. Explicit routes — registered race-free before the first request
Server server = CoreUIServiceJava
.startServer("127.0.0.1", 8080, List.of(MyView.class, AdminView.class))
.getOrThrow();
// 3. Scanned routes — classgraph walks given packages on the runtime classpath
Server server = CoreUIServiceJava
.startServer("127.0.0.1", 8080,
CoreUIServiceJava.scanForRoutes("com.example.views", "com.example.admin"))
.getOrThrow();That’s the surface. Everything else is config-as-code.
First-class failure handling
startServer(...) returns a Result<Server, Exception> (from
functional-reactive),
so a port-bind failure or missing config is a first-class return value
rather than a thrown checked exception:
CoreUIServiceJava.startServer("127.0.0.1", 8080)
.peek(srv -> logger().info("listening on {}", srv.getURI()))
.peekFailure(err -> logger().error("startup failed", err));Fail-fast bundle probe
The library probes the classpath for META-INF/VAADIN/webapp/index.html
before booting Jetty. If the frontend bundle is missing,
startServer(...) short-circuits to a Result.failure(IllegalStateException)
with a message naming the missing path and pointing at the right Maven
goal — you don’t have to debug Vaadin’s runtime HTTP 500 / Unable to find index.html mystery anymore.
When to use it
✅ Internal tooling, ops dashboards, one-shot utilities
✅ Demos where Spring Boot is overkill
✅ Anything you want to ship as a single java -jar binary
✅ Embedded scenarios (Raspberry Pi, IoT gateways)
❌ Production apps where you already have Spring/Quarkus in the stack — use those ❌ When you need a full WAR deployment lifecycle
Status
Active. Updated regularly as Vaadin Flow and Jetty release new versions. Currently tracking Vaadin Flow on Jetty 12 (EE11) with Java 26.
License is EUPL-1.2 — copyleft-style, EU-approved equivalent to LGPL.