A Java reactive UI framework without reactive streams

You change a value somewhere in your Java code, and you want the screen to follow without hand-writing refresh calls for every label and every table. That is what most developers mean when they search for a Java reactive UI framework, and it rarely has anything to do with reactive streams.
webforJ, the full-stack Java framework, is a Java reactive UI framework in the sense that matters for screens: the UI reacts to events, to bound data and to repository commits, and background work can push changes to the page. It does this with event listeners and data binding, not with a streams library, and this post shows how those pieces fit together.
The webforj-databinding app in the built-with-webforJ collection binds one form to an employee record with nested address and contact details, and enables its Save button only while the form is valid. It runs with Java and Maven.
What does "reactive" mean for a Java UI?
The word covers two different ideas. One is the reactive-streams programming model, with publishers, subscribers, and back pressure. The other is a UI behavior: when state changes, the parts of the screen that show that state update.
Developers who search for a reactive UI framework usually want the second one. They want to stop writing code like this: after saving, find the table and reload it, then find the counter and update it. They want the wiring to live in one place.
webforJ is event driven and binding based. It is not a reactive-streams library, and you do not need to learn operators to get a UI that follows its data. If you already use a streams library on the server, you can keep it and feed its results into the UI, which the last section covers.
How does a webforJ UI react to events?
Every component exposes listeners for what happens to it: clicks, value changes, selections, and more. You register a lambda or a method reference and the framework calls it when the event occurs. The events guide lists the patterns, including custom events you dispatch yourself.
This is the base layer. A value change on a field can enable a button, filter a table or recalculate a total, and the code that does it sits next to the component that triggers it. Because components live on the server, the handler is plain Java with full access to your domain objects, and there is no client-side store to keep in sync.
Is there data binding in Java UIs?
Yes, and this is the part that answers the MVVM question. In webforJ, BindingContext connects UI components to the properties of a Java bean. The framework reads bean values into the fields, writes valid field values back to the bean, and runs validation in between. The data binding overview walks through a complete example.
The simplest setup is automatic binding. BindingContext.of matches the components you declared as fields with the bean properties of the same name, and UseProperty maps the ones whose names differ, including nested paths such as address.street. See automatic binding for the annotations.
For continuous synchronization, the observe method registers a value change listener on every binding. User edits go to the bean as soon as they are valid, and a validate listener keeps the UI state, such as the submit button, aligned with the form:
Hero bean = new Hero("Superman", "Fly");
BindingContext<Hero> context = BindingContext.of(this, Hero.class, true);
context.observe(bean);
context.onValidate(e -> {
submit.setEnabled(e.isValid());
});
Nothing in this code mentions a specific field. Add a property to the bean and a matching component to the view, and the binding picks both up.
Can you build MVVM in webforJ?
The core of MVVM is a view that shows a model and edits it through a binding layer, with the logic kept out of the view. In webforJ, the bean is the view model, the view is a composite of components, and BindingContext is the binding layer between them. Validation rules live on the bean or on the binding, so the same rules apply wherever the bean is edited.
You can also go further and split the logic out of the view entirely. The MVC todo app post builds that structure with a separate controller, and the same layering works with a bean as the view model.
Where this differs from a client-side MVVM framework is the transport. The view model is a server-side Java object, so there is no JSON contract to maintain between a browser store and a backend. That removes a whole class of mismatches between what the UI thinks the state is and what the server thinks it is.
How do tables refresh when data changes?
Tables and lists in webforJ read from a Repository. When your data changes, you tell the repository with commit(), and every connected component updates. The repository overview explains the two forms. commit() refreshes everything, and commit(entity) updates only the row for that entity, found by its key.
Every commit also fires an event, so you can react to data changes elsewhere on the screen. The events and updates page shows summary labels that stay in sync this way:
// Auto-updating labels
repository.onCommit(event -> {
double total = sales.stream().mapToDouble(Sale::getAmount).sum();
totalLabel.setText(String.format("Total: $%.2f", total));
countLabel.setText("Sales: " + sales.size());
});
The listener fires on every commit, whether it came from a user action, an import, or code. The same page covers removing listeners with the ListenerRegistration that onCommit returns, which you should do when a view goes away.
How do background jobs update the UI?
Sometimes the change does not come from the user. A job finishes, a message arrives, or a timer fires. webforJ keeps all UI work on the session's Environment thread, so background code asks the framework to run the update there with Environment.runLater. You read the target Environment while you are still on the UI thread, hand it to the background work, and pass it to runLater when you have a result. The asynchronous updates guide covers both forms of the call and the common mistake of reading the environment on the wrong thread.
For recurring refreshes, Interval fires an elapsed event at a fixed delay. You attach a listener, start it, and refresh the data in the handler. The Interval guide lists live data and inactivity checks as typical uses.
Combine the pieces and you get the full loop. Background work calls runLater, the update changes a bean or the repository, and the binding or the commit carries that change into the components. You did not write the layer that ships the change to the browser.
If your server code already produces results through a streams library or any other async source, treat its callback as one more background thread. Capture the Environment first, then call runLater from the callback.
Which mechanism fits which job?
| You want to | Use | Docs |
|---|---|---|
| Run code when a user acts | Component event listeners | Events |
| Keep a form and a bean in sync | BindingContext with observe | Automatic binding |
| Refresh a table or a summary | Repository.commit() and onCommit | Events and updates |
| Update from a worker thread | Environment.runLater | Asynchronous updates |
| Refresh on a schedule | Interval | Interval |
Each row is a small, separate tool, and they compose. A form bound to a bean can sit next to a table backed by a repository, and a single Interval handler can update both.
Try it in your own project
Generate a project with startforJ, add a form view, and bind it to a bean with BindingContext.of. Then add a repository-backed table beside it and call commit() after a save. Two views, one bean and one repository are enough to see the screen follow the data. The getting started guide covers the setup.
Frequently asked questions
Is webforJ a reactive framework?
webforJ is event driven and binding based, so the UI reacts to events, bound bean values, and repository commits. It does not ship a reactive-streams API, and you do not need one to keep a screen in sync with its data.
Can I use the view model pattern in webforJ?
Yes, in the form most Java teams use. A bean acts as the view model, components form the view, and BindingContext handles the two-way binding and validation between them. The view model stays a plain server-side Java object.
How do I update the UI from another thread?
Read the session's Environment on the UI thread, pass it to your background work, and wrap each UI change in Environment.runLater. The framework then runs the change on the correct thread for that user.
Bouw je iets met webforJ?
Bespreek je project, migratie of de geschiktheid van webforJ met het team.



