Swing modernization
Swing to the web, one screen at a time
Your Swing, JavaFX or SWT app runs in a web browser without changes to its source. Then you build new screens in webforJ around it and replace the old ones at your own pace. The Java team and the code stay.
- Desktop toolkits: Swing, JavaFX and SWT
- 3
- Source changes to run in a browser
- 0
- CustomersSwing
- OrderswebforJ
- ReportsSwing
What stays the same
Only the screens change
webforJ is the full-stack Java framework for business web apps: you build the whole app, screens included, in Java. So moving a Swing app means moving its screens, and everything under them carries over.
What changes
The screens. You move them one at a time, in the order that matters to your business, while the rest of the Swing app keeps running in the same browser tab.
- 01
The team
The people who maintain the Swing app today build the web screens in Java. There is no second team and no front-end toolchain to learn.
- 02
The code
Webswing runs the app as it is. The same code base keeps working as a desktop app and in the browser, so you maintain one app, not two.
- 03
The logic
Domain classes, services and validation rules that have nothing to do with
javax.swingare called straight from the new screens. They do not become endpoints first. - 04
The toolchain
Maven, your IDE, your debugger and your existing tests continue to apply.
The move, in three phases
Step through the move from desktop to web
Each phase ships something users can see. Pick a phase to see the screen at that point and the code that makes it work.
Run the desktop app in the browser
Webswing runs your Swing, JavaFX or SWT app on the server and streams its interface to the browser as HTML5 canvas. Mouse and keyboard input go back to the real app. The source code does not change.
Embed it in a webforJ view with WebswingConnector. The connector connects to the Webswing server when it is added to the page.
@Route
public class SwingAppView extends Composite<Div> {
private final Div self = getBoundComponent();
private WebswingConnector connector;
public SwingAppView() {
// Initialize the connector with your Webswing application URL
connector = new WebswingConnector("http://localhost:8080/myapp/");
// Set the display dimensions
connector.setSize("100%", "600px");
// Add to the view container
self.add(connector);
}
}
Phase 1 of 3
Two-way communication
Old and new screens talk to each other
A message is an action: a name, an optional string payload and an optional binary payload. The name is yours to invent. Both ends are plain Java, so nobody writes browser script for the exchange.
- 1
Swing announces
A double click sends
select-customerwith the row as JSON. On the desktop the same click still opens the Swing dialog.Application.java (Swing)private void handleDoubleClick(MouseEvent e) {int row = table.rowAtPoint(e.getPoint());if (row >= 0 && row < customers.size()) {Customer customer = customers.get(row);if (isWebswing) {api.sendActionEvent("select-customer", gson.toJson(customer), null);} else {showEditDialog(customer);}}}CustomerBridgeView.java (webforJ)connector.onAction(e -> {switch (e.getActionName()) {case "select-customer" -> e.getActionData().ifPresent(this::openEditor);case "export-ready" -> e.getActionBinaryData().ifPresent(d -> status.setText("Got " + d.length() + " characters"));default -> { }}}); - 2
webforJ replies
On save the web form sends
update-customerback, and the Swing app refreshes its table. Neither side calls into the other one's internals.Application.java (Swing)private void setupWebswingListeners() {api.addBrowserActionListener(event -> {if ("update-customer".equals(event.getActionName())) {Customer updated = gson.fromJson(event.getData(), Customer.class);updateCustomer(updated);}});}CustomerTableView.java (webforJ)dialog.onSave(() -> {// Send updated customer back to Swingconnector.performAction("update-customer", gson.toJson(customer));});
Send commands into Swing
performAction() sends a name alone or a name with data. The Swing app decides what each name means.
// Trigger a refresh in the Swing application from webforJ
connector.performAction("refresh");
// Send a command with data from webforJ
connector.performAction("selectRecord", "12345");
Know when Swing is ready
onInitialize fires when the connection is ready. onStart fires when the Swing app has fully loaded. Send your first action from onStart.
connector.onInitialize(e -> status.setText("Connected"));
connector.onStart(e -> status.setText("Swing app running"));
Port with an AI assistant
Let an assistant port it, screen by screen
Moving screen by screen is repetitive work, and that suits an AI coding assistant. The webforJ AI plugin connects it to the webforJ MCP server and to Agent Skills, structured workflows that tell it when to use those tools, in what order, and how to validate the result.
- 1
Give it one Swing screen
Ask for the webforJ equivalent that calls your existing services.
- 2
It checks the real API
The MCP server looks up webforJ documentation and API details on demand, instead of guessing.
- 3
You review and test
Compare behavior against the Swing original, then move on to the next screen.
How to start
From a JAR file to a page in the browser
You need a Swing, JavaFX or SWT app packaged as a JAR file, the Webswing server and webforJ 25.10 or later.
- 1
Download and start Webswing
Download Webswing, extract the archive, then start the server with the script for your platform. It answers on
http://localhost:8080.
webswing.sh - 2
Register your app
In the admin console at
http://localhost:8080/admin, set the application name, main class, classpath, JVM arguments and home directory. The app is then served athttp://localhost:8080/[app-name]/.- CORS
- Add your webforJ app's origin to Allowed Origins, for example
http://localhost:8090. - Ports
- Both typically use 8080. Change one of them so they do not conflict.
- 3
Add the dependency
The webforj-webswing module provides WebswingConnector and the classes around it.
<dependency><groupId>com.webforj</groupId><artifactId>webforj-webswing</artifactId><version>${webforj.version}</version></dependency> - 4
Embed and configure
Add a WebswingConnector to a view, as in phase 1. WebswingOptions changes its behavior, for example hiding the logout button when your webforJ app handles sign in.
WebswingConnector connector = new WebswingConnector("http://localhost:8080/myapp/");WebswingOptions options = new WebswingOptions().setDisableLogout(true); // Hide the logout buttonconnector.setOptions(options);
Pick your first screen
Start with a simple list and edit form. Embed the app, move that one screen to webforJ, and see how it runs next to the rest.
- Webswing overviewWhat Webswing solves and how the three layers work together.
- Setup and configurationServer install, admin console, CORS and the connector options.
- CommunicationEvery action and event between webforJ and the Swing app.
- Modernization tutorialA Swing customer table, embedded and given a webforJ edit dialog.
- webforJ AI pluginThe MCP server and Agent Skills for your coding assistant.
- Docs homeEverything else in webforJ, from the first project to the components.