Vaadin Flow 25.2 brings server-side Java APIs for a broad set of browser capabilities — Geolocation, Clipboard (copy and paste), Fullscreen, Screen Wake Lock, Screen Orientation, Web Share, and Page Visibility — together with a new client-side action/trigger framework for wiring browser behavior to server logic without hand-written JavaScript. The reactive Signals API gains router and page-visibility signals, routing adds stateless route hierarchies and dynamic page titles, and the release hardens security defaults (X-Frame-Options, URL-scheme validation, npm supply-chain delay). It also ships major tooling upgrades: Vite 8 (Rolldown), Spring Boot 4.1, TypeScript 6, and JUnit Jupiter 6.1.
Breaking Changes
-
Stop auto-running
vaadinPrepareFrontendin development mode (Gradle)
Pull request · IssueIDE-triggered Gradle builds no longer run
vaadinPrepareFrontendviaprocessResources, which previously interfered with the running Vite dev server and broke hot deploy. Since Vaadin 25 the dev server prepares the frontend at runtime. Restore the old behavior withalwaysExecutePrepareFrontend = true. -
Auto-apply
context://for@StyleSheetvalues
Pull requestBare relative
@StyleSheethrefs now resolve against the context root instead of<base>, fixing 404s whenvaadin.urlMappingis set to a non-root path.http(s)://,//,context://,base://, and/-prefixed values pass through unchanged; path traversals are rejected. -
NativeLabel.setFor()lazily resolves and auto-generates IDs
Pull requestsetFor(Component)now resolves the target's ID lazily at sync time, auto-generating one if missing, so developers no longer need to assign IDs manually. -
Secure-by-default behavior changes — see Security:
X-Frame-Optionsis now sent asSAMEORIGINby default, andAnchor/IFrame/Page#openreject script-capable URL schemes (javascript:,data:) unless the unsafe variants are used.
New Features
Browser API Wrappers
-
Geolocation API
Pull requestA new
Geolocationutility wraps the browser Geolocation API: one-shot reads viagetPosition(onSuccess, onError)and continuous tracking viawatchPosition(...), all asynchronous and delivered on the UI thread. Results use sealed types (GeolocationPosition|GeolocationError, plusGeolocationPendingfor the watcher signal) for exhaustive pattern matching. Watches auto-stop on detach;stop()/resume()are explicit. AGeolocationClientFactoryLookup SPI lets external/browserless test drivers swap the client.
Follow-ups: #24270, #24268, #24211, #24259, #24279Button locate = new Button("Use my location"); locate.addClickListener(e -> Geolocation.getPosition( pos -> showNearest(pos.coords().latitude(), pos.coords().longitude()), err -> showManualEntry()));
-
Clipboard API (write, read, and paste)
#23615 · #24551 · #24552 · #24570 · #24583A
Clipboardentry point binds clipboard access to a user gesture.Clipboard.onClick(component)produces aClipboardBindingfor writing plain text, HTML, field values,image/png, and multi-formatClipboardContent, with fire-and-forget and observed (onCopied/onError) variants — plus a matchingread/readText/readHtmlside.Clipboard.onPaste(...)forwards native paste events to a server-side listener, andClipboard.onFilePaste(...)streams pasted files through anUploadHandler.Button copy = new Button("Copy"); Clipboard.onClick(copy).writeText(textField, copied -> Notification.show("Copied " + copied), err -> Notification.show("Failed: " + err.message()));
-
Fullscreen API
Pull requestEnter, exit, and observe fullscreen state for the whole page or a single component. Adds the
Fullscreenentry point, theFullscreenBindingfluent binding, theFullscreenStateenum, and per-UI server-side state synchronization. -
Screen Wake Lock API
Pull requestA per-UI wake-lock facade via
Page#getWakeLock()exposingrequest(),release(), and an active-stateSignal<Boolean>. The client transparently re-acquires the lock onvisibilitychange. -
Screen Orientation API
Pull requestOrientation-change monitoring plus lock/unlock operations through
Page. -
Web Share API
Pull requestA
WebShareAPI to invoke the browser's native share dialog, with feature detection and support for sharing dynamic content sourced from components.
Action / Trigger Framework (preview)
A new client-side trigger/action framework lets you wire browser events and inputs to server-side state and actions without hand-written JavaScript. It is intended as low level API to build higher level features on.
DownloadAction— trigger browser downloads from static URLs, server streams, or client-resolved values #24544OpenInNewTabAction— open URLs in a new tab/window, withjavascript:blocked on both sides #24545CallbackAction/SetSignalAction— forward a client value back to aSerializableConsumer/ aSignalon the UI thread #24455SignalInput— read a server-sideSignalat trigger fire time #24456SizeTrigger— fire on element resize viaResizeObserver, exposingwidth()/height()/size()#24492- Public input APIs —
Action.Inputrendering andHandlerInputare public so custom actions/triggers outside Flow can consume trigger inputs #24648
Routing and Navigation
-
UI router state signal
Pull requestUI.routerStateSignal()is a read-onlySignal<RouterState>(currentLocation,RouteParameters, active chain, target class) updated atomically alongsideAfterNavigationEvent, so components can observe the active route viaSignal.effectinstead of registering anAfterNavigationListener. -
Route hierarchy with dynamic titles
Pull requestAn instance-free mechanism for resolving page titles and logical route hierarchies — enabling dynamic titles and navigation aids like breadcrumbs and menus without instantiating navigation targets. Adds the
@DynamicPageTitleand@RouteParentannotations, thePageTitleGeneratorinterface, andRouteConfigurationmethods to resolve a route's parent and hierarchy statelessly. -
Expose page-title resolution as public API
Pull requestRouter.resolvePageTitleresolves a navigation target's title without instantiating it, applying the same generator chain used during navigation.
Reactive Signals
-
Add
pageVisibilitySignal()toPage
Pull requestA read-only signal tracking whether the browser tab is visible+focused, visible+not-focused, or hidden.
-
Bulk insert methods for
ListSignal/SharedListSignal
Pull requestinsertAllLast,insertAllFirst, andinsertAllAtapply a batch with a single notification (and transactionally for shared signals).
Components and Events
- Add
getUI()toComponentEvent#24205 and support an explicit UI in the constructor #24262 — no moreevent.getSource().getUI().ifPresent(...)boilerplate. - Introduce
HasComponentsOfTypefor typed child containers #24186 - Drag-and-drop to a specific location — exposes
clientX/clientYand element offsets for all D&D events #23187 - Virtual-aware component tree traversal —
ComponentUtil.getAllChildren/streamDescendantsinclude virtual children (slotted helpers, overlays, routing wrapper) #24459 setTestId/getTestIdconvenience methods onComponent#23775
executeJs and Client-Side JS
JsFunctionvalue type for composableexecuteJs— build a JS function value with captured parameters (including Elements and nestedJsFunctions) and pass it as a parameter, removing string-concatenated boilerplate #24374Element.addJsInitializerfor scoped client-side JS lifecycle #24366
Server, Session, and RPC
UI.triggerAfter(Duration, SerializableRunnable)— run a server task after a browser-measured delay without enabling push; the returnedRegistrationcancels the timer #24586- RPC invocation listener support #24613
- Session lock request/acquire/release events #24604
Data and Binding
- Extract
HierarchicalTreeDatainterface fromTreeData— implement the interface to useHierarchicalTreeDataProviderwithoutTreeData; no existing API changed #23950 bindItemsforHasDataProvidercomponents #23761- Targeted item refresh —
refreshItem(newItem, oldItem)re-maps identity when signal-bound items are replaced #23913 Binder.setAcceptHiddenFields(boolean)— restore pre-Vaadin-25 binding of hidden fields #24139fromInputStreamoverload withfileNameOverride#24044
Security
X-Frame-Options: SAMEORIGINby default — opt into clickjacking protection out of the box; configurable via theframeOptionsinit parameter (empty disables it) #24589- URL-scheme validation in
Anchor,IFrame, andPage#open— safe schemes default tohttp,https,mailto,tel,ftp; script-capable schemes are rejected. Each sink offers an unsafe variant (setUnsafeHref,setUnsafeSrc,openUnsafe) for trusted hard-coded URLs #24588 Safelistoverloads on theHtmlcomponent #24580- npm supply-chain delay — a minimum-package-age check (default 1 day) instructs npm/pnpm/bun not to install package versions newer than the threshold, mitigating freshly-published compromised releases. Configurable via
Options#withMinimumPackageAgeDays(int)(0 disables) #24338, #24334
Build, Frontend, and Tooling
- Upgrade Vite 7 → Vite 8 (Rolldown) — Vite 8 replaces esbuild/Rollup with the Rust-based Rolldown bundler #23893
- Upgrade to TypeScript 6 #23993
BuildFrontendincremental build for faster development cycles #23884- Content-hash cache busting for
@StyleSheetURLs — appends?v-c=<hash>in production so versioned stylesheets can be cachedimmutablefor a year #23544 - Local web components plugin — auto-redirects
polymer/*andvaadin/*imports to a projectweb-components/node_modulesfolder in dev mode #23793 helpgoal for the Maven plugin #24106SignalBindingwith a richBindingContext— allElementbind methods now return aSignalBindingexposing anonChangecallback with old/new value and initial-run state #23670- Shade ASM into
flow-build-toolsto avoid clashes with projects on older ASM versions #24025 - Expose application properties to
TypeScriptBootstrapModifierconsumers #24073
Notable Fixes
- Prevent deadlocks in
AtmospherePushConnectionon concurrent push/disconnect #24215, #24133 - Jetty 12.1 static-resource loading — fix
FileSystemNotFoundExceptionfor resources packaged in JARs (e.g.vaadinPush.js) #24283, and sanitize%in resource URLs rejected by Jetty 12 #24031 - Handle Unicode (incl. decomposed) classpath resource paths that previously failed startup #24220
- Detect incompatible Jackson at startup — surface a clear error instead of a cryptic
NoSuchMethodErrorwhen Jackson < 3.1 is on the classpath #24009 - Show a validation error instead of
NullPointerExceptionwhennullis bound to a primitive bean property #24266 - Preserve the
slotattribute for initially-invisible elements so structural CSS selectors keep working #24037 - Stable
package.jsonhash across Linux and Windows #24321 - Prevent stale JAR caches under the Gradle daemon #23859 and Maven daemon (mvnd) #23863; delete
flow-build-info.jsonafter the Maven session #23945 - Preserve scroll positions of all scrollable elements after hot-swap #23722; retain local signal values during hot-swap #23854
- Load
Image/IFramesources backed by aDownloadHandlereven inside a disabled component #24346 - Fix
NullPointerExceptionon expired web-push subscriptions #24310 - Numerous Signals correctness fixes — avoid duplicate/stale observers on concurrent effect revalidation #24607, prevent double effect invocation when reading the same signal multiple times #23978, and avoid false infinite-loop detection when a cached signal self-updates #24412
Testing Improvements
This release continues the migration to JUnit 5/6 (Jupiter) across the codebase — including flow-data, flow-build-tools, flow-polymer-template, and vaadin-dev-server — improving test maintainability and leveraging modern testing features.
Dependency Updates
- ⚠️ Spring Boot updated to 4.1.0 — requires Jackson 3.1+; earlier Spring Boot 4.0.x ships Jackson 3.0 and is incompatible.
- Vite updated to 8.0.16 (Rolldown-based), @vitejs/plugin-react to 6.0.2
- TypeScript updated to 6.0.2
- Node.js updated to 24.16.0
- pnpm updated to 11.6.0
- Tailwind CSS updated to 4.3.1
- Jackson updated to 3.1.3
- JUnit Jupiter updated to 6.1.0
- TestBench updated to 25.2.0
- Jetty updated to 12.1.9
- Maven updated to 3.9.16
- Guava updated to 33.6.0-jre
- Frontend dependencies updated to latest versions
Thanks For Contributions!
@martinfrancois @wutzebaer @sclassen @adraarda23
For more details, see the full comparison on GitHub.