Zum Hauptinhalt springen

What's new in version 26.01?

· 8 Min lesen
webforJ Team
webforJ Development Team

cover image

webforJ 26.01 is live! The headline is a new frontend bundler: a Bun-powered build step wired into the Maven and Gradle build you already run, letting a Java view pull in npm packages and web components without a separate frontend project or a Node toolchain. Alongside it, this release reworks live reload, adds a new Upload component, brings browser APIs for geolocation, page visibility, and app icon badges, and gives AppNav pinning and search. See the highlights below, and as always, the GitHub release overview has the complete picture.

Frontend bundler

webforJ 26.01 introduces a frontend bundler that brings the npm ecosystem into reach from Java. A view names the frontend packages it needs with an annotation, and the build takes care of the rest, with no separate frontend project to manage and no Node toolchain to install. It ships in the new webforJ build plugin for Maven and Gradle, added once:

New projects, no setup

New webforJ archetypes ship with it preconfigured; existing projects opt in incrementally.

pom.xml
<plugin>
<groupId>com.webforj</groupId>
<artifactId>webforj-maven-plugin</artifactId>
<extensions>true</extensions>
</plugin>

Pulling a web component into a webforJ app used to mean wiring up its assets by hand. Now you name an npm package and use its components straight from Java. The simplest case writes no frontend code at all. Here, a view drops in two UI5 Web Components:

Ui5View.java
@Route("/ui5")
@BundlePackage(value = "@ui5/webcomponents", version = "^2.0.0")
@BundleEntry("@ui5/webcomponents/dist/Button.js")
@BundleEntry("@ui5/webcomponents/dist/Input.js")
public class Ui5View extends Composite<FlexLayout> {
private final FlexLayout self = getBoundComponent();

public Ui5View() {
self.setAlignment(FlexAlignment.CENTER).setStyle("margin", "1em");

Element input = new Element("ui5-input");
input.setProperty("placeholder", "Type something");

Element button = new Element("ui5-button");
button.setProperty("design", "Emphasized");
button.setText("Say hello");
button.addEventListener("click", e ->
Toast.show("Hello " + input.getProperty("value")));

self.add(input, button);
}
}

A couple of annotations, the rest plain Java, and the component renders on the page. When a prebuilt library isn't enough, you can bring your own TypeScript components and use them the same way.

Styling comes along with it. Author SCSS or Less directly, and Tailwind is one setting away. Turn it on, and a view applies utility classes straight from Java, with nothing to import:

TailwindView.java
@Route("/tailwind")
public class TailwindView extends Composite<FlexLayout> {
private final FlexLayout self = getBoundComponent();

public TailwindView() {
Div card = new Div("Styled by compiled Tailwind utilities");
card.addClassName("flex", "items-center", "gap-4", "p-8", "rounded-lg",
"bg-blue-500", "text-white");
self.add(card);
}
}
Learn More

See the webforJ build plugin, Frontend bundler, and Frontend watch docs for setup, options, and live-reload keys.

The AppNav component picks up two features for larger menus: pinning moves a user's most-used items into a group at the top of the nav, and search filters the menu as they type.

AppNav nav = new AppNav("main-nav");

nav.getPinning().setEnabled(true);
nav.getPinning().setAutosave(true); // persists to browser local storage

nav.getSearch().setFieldVisible(true);
nav.getSearch().setPlaceholder("Search");

The pin toggle appears on hover and keyboard focus, unpinning returns an item to its original position, and pins persist across reloads with no server code.

Search opens any group containing a match and keeps pinned items visible while filtering.

Code anzeigen

Learn More

See the AppNav documentation for pin events, custom persistence, and driving search from your own input.

Upload component

26.01 adds the Upload component, an inline file picker that sits directly in a layout alongside the rest of a form.

Upload upload = new Upload();
upload.addFilter("Images", "*.png;*.jpg");
upload.addFilter("Documents", "*.pdf;*.docx");
upload.setSelectionMode(Upload.SelectionMode.MULTIPLE);
layout.add(upload);

It supports drag-and-drop, named filters, single or multiple selection, and directory uploads out of the box. On browsers that support the File System Access API, users get the native OS picker.

Learn More

See the Upload documentation for filters, selection modes, drop-zone behavior, and event tracking.

Nested bean binding

Data binding reaches into nested objects in 26.01. A binding property can now be a dotted path into a nested bean:

BindingContext<Person> context = new BindingContext<>(Person.class);
context.bind(streetField, "address.street").add();
context.bind(cityField, "address.city").add();

Reads resolve safely through null intermediates, writes create any missing intermediate beans automatically, and Jakarta validation annotations are detected the same way as on top-level properties.

Learn More

See the Bindings documentation for the full rules around reads, writes, and validation on nested paths.

Browser APIs

26.01 gives three browser capabilities first-class Java APIs.

Geolocation

The browser's Geolocation API gets a Java binding. Request a one-shot position, or register a watch listener and stream updates as the device moves:

PendingResult<GeolocationPosition> request =
Geolocation.getCurrent().getCurrentPosition();

request.thenAccept(position -> {
double lat = position.getLatitude();
double lng = position.getLongitude();
double accuracy = position.getAccuracy();
});

High-accuracy mode, timeouts, and a max-age cache are available when you need them. See the Geolocation documentation for the full API, configuration, and a complete example.

App icon and favicon badges

Two APIs paint notification badges onto the surfaces users watch: the OS app icon and the browser tab favicon.

App.setBadge(5); // OS app icon (dock, taskbar, home screen)
Page.getCurrent().setIconBadge(5); // Browser tab favicon

App.setBadge updates the macOS dock, the Windows taskbar, and the Android home screen once the app is installed. Page.setIconBadge paints the same count onto the favicon, with no installation required. Color, shape, and size are configurable through IconBadgeOptions. See the App badges documentation for prerequisites, browser support, and styling.

Page Visibility

The Page Visibility API tells your code when the user switches tabs, minimizes the window, or comes back. Use it to pause polling while the page is hidden, send a desktop notification when the tab loses focus, or refresh stale data when focus returns:

Page.getCurrent().onVisibilityChange(event -> {
if (event.isHidden()) {
pauseRendering();
} else {
resumeRendering();
}
});

See the Page Visibility documentation for the full API and a worked example that pairs it with desktop notifications and favicon badges.

Application security

Securing a webforJ app starts with the framework itself. Its server-driven model keeps your logic and data on the server, away from the browser. The new Application Security guides cover everything around that, walking through how threats like XSS, CSRF, SQL injection, and executeJs injection apply to a webforJ app, how to harden a deployment with TLS, server-side validation, secure-by-default routes, and dependency upkeep, and how to keep passwords, API keys, and tokens out of your repository, with Spring Boot patterns included.

Cookbook

26.01 opens the webforJ Cookbook, a new home for short, self-contained recipes you can lift straight into an app, each one verified to compile and run. The opening set works through the patterns that keep coming up: editing a record in a dialog with BindingContext, debouncing email validation, persisting Table column widths per user, and blocking navigation away from unsaved changes. It grows with every release.

Deprecation: <html> in setText

Components that show text expose setText for plain text and setHtml for HTML. In earlier versions, a value wrapped in <html> and passed to setText was rendered as markup. That legacy behavior is now deprecated, and it will be removed in webforJ 27.

Nothing breaks in 26.01. The webforj.legacyHtmlInText setting still defaults to true, so <html> wrapped text keeps rendering as HTML, and the first time such a value reaches setText webforJ logs a warning naming the component and the call site so you can find it. To pick up the webforJ 27 behavior early, set webforj.legacyHtmlInText to false and those values render as literal characters instead. The lasting fix is to pass HTML through setHtml.

The webforJ rewrite recipes take care of most of this, rewriting an <html> wrapped setText literal to setHtml wherever the component supports it.

Learn More

See the Automated Upgrades guide for the OpenRewrite recipes that migrate these calls for you.


That's 26.01 in a nutshell! For the complete changelog, see the GitHub release.