How to Build Modern Java Desktop Apps with Shadcn UI, React, and JxBrowser
This article explains why traditional Java UI toolkits fall short, then walks through a complete solution that embeds a React + shadcn/ui front‑end inside a Java desktop window using JxBrowser, covering window creation, resource loading for dev and production, and two Java‑Web communication strategies (JS‑Java bridge and Protobuf + gRPC).
Swing/JavaFX Limitations
Java native UI toolkits (Swing, JavaFX, SWT) have three major drawbacks: difficult UI customization, a sparse component ecosystem, and an outdated visual style that has not changed for a decade. Web UI provides abundant component libraries, built‑in high‑DPI, touch and responsive support, and cross‑platform visual consistency.
Overall Solution: JxBrowser + React + shadcn/ui
The goal is to build a preferences dialog that persists user settings to the local file system and restores them on restart. The stack combines a Chromium‑based JxBrowser view, a React application styled with shadcn/ui, and TypeScript.
Window and Web View
A JFrame is created and a JxBrowser Chromium view is embedded inside it:
var engine = Engine.newInstance(HARDWARE_ACCELERATED);
var browser = engine.newBrowser();
SwingUtilities.invokeLater(() -> {
var view = BrowserView.newInstance(browser);
var frame = new JFrame("Application");
frame.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
engine.close();
}
});
frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
frame.add(view, BorderLayout.CENTER);
frame.setSize(1280, 900);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
});Resource Loading: Development vs Production
Development
A development server provides hot‑reloading. The browser loads the UI from the local server:
./gradlew startDevServer
if (!AppDetails.isProduction()) {
browser.navigation().loadUrl("http://localhost:[port]");
}Production
Running a dev server in production adds an extra process and exposes source files via localhost. The solution packages web assets into the JAR and serves them through a custom jxb:// scheme using JxBrowser’s request interceptor:
var options = EngineOptions.newBuilder(HARDWARE_ACCELERATED)
.addScheme(Scheme.of("jxb"), new UrlRequestInterceptor());
var engine = Engine.newInstance(options.build());Navigation then loads resources from the classpath:
if (!AppDetails.isProduction()) {
browser.navigation().loadUrl("http://localhost:[port]");
} else {
browser.navigation().loadUrl("jxb://my-app.com");
}Java ↔ Web Communication
Two approaches are presented for the web front‑end to invoke Java code that reads or writes preference files.
Option 1: JS‑Java Bridge (small projects)
JxBrowser allows JavaScript to call annotated Java methods directly:
@JsAccessible
class PrefsService {
void setFontSize(int size) { }
}
// TypeScript side
declare class PrefsService {
setFontSize(size: number): void;
}
const prefsService = new PrefsService();
prefsService.setFontSize(12);This works for a few calls but lacks compile‑time checks and IDE completion as the interface grows.
Option 2: Protobuf + gRPC (serious projects)
A .proto file defines a type‑safe API, generating Java and TypeScript stubs:
service PrefsService {
rpc SetFontSize(FontSize) returns (google.protobuf.Empty);
}
enum FontSize {
SMALL = 0;
DEFAULT = 1;
LARGE = 2;
}The Java side runs a gRPC server, while the web side uses a Connect client:
// Java server
class PrefsService extends PrefsServiceImplBase {
@Override
public void setTheme(Theme request, StreamObserver<Empty> responseObserver) { }
}
// TypeScript client
const transport = createGrpcWebTransport({
baseUrl: `http://localhost:50051`,
});
const prefsClient = createClient(PrefsService, transport);
prefsClient.setFontSize(FontSize.SMALL);Benefits include type safety, automatic code generation, IDE autocomplete, and compile‑time validation, which become increasingly valuable as the project scales.
Reference
Full source code is available on GitHub: https://github.com/TeamDev-IP/JxBrowser-Gallery
SpringMeng
Focused on software development, sharing source code and tutorials for various systems.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
