How to Build a Java Desktop App with Shadcn UI, React, and JxBrowser
This article demonstrates how to create a cross‑platform Java desktop application by embedding a React + TypeScript UI built with shadcn/ui inside a Chromium‑based JxBrowser view, covering window setup, resource loading for development and production, and two Java‑Web communication strategies (JS‑Java bridge and Protobuf + gRPC).
Swing/JavaFX Limitations
Java native UI toolkits (Swing, JavaFX, SWT) share three drawbacks:
UI pain : implementing modern animated transitions requires custom components.
Ecosystem desert : few component libraries, low community activity, hiring difficulty.
Outdated look : default controls appear decade‑old.
Web UI provides abundant component libraries, built‑in high‑DPI, touch, responsive support, and cross‑platform consistency.
Overall Solution: JxBrowser + React + shadcn/ui
Goal: a preferences dialog that persists user settings to the local file system across restarts. Embed a React + TypeScript UI built with shadcn/ui inside a Chromium‑based JxBrowser view hosted in a Swing JFrame.
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 Environment
Run a local dev server; the browser loads the UI from http://localhost:[port] with hot‑reloading.
./gradlew startDevServer
if (!AppDetails.isProduction()) {
browser.navigation().loadUrl("http://localhost:[port]");
}Production Environment
Production cannot rely on a dev server. JxBrowser’s custom scheme interceptor packages web assets into the JAR and serves them via a jxb:// protocol.
var options = EngineOptions.newBuilder(HARDWARE_ACCELERATED)
.addScheme(Scheme.of("jxb"), new UrlRequestInterceptor());
var engine = Engine.newInstance(options.build());When the browser requests jxb://my-app.com, the interceptor returns index.html and subsequent CSS/JS files from the classpath, keeping resources internal while normal HTTPS/API requests remain unaffected.
if (!AppDetails.isProduction()) {
browser.navigation().loadUrl("http://localhost:[port]");
} else {
browser.navigation().loadUrl("jxb://my-app.com");
}Java ↔ Web Communication
The web front‑end must invoke Java code to read/write the preferences file. Two approaches are presented.
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;
}
prefsService.setFontSize(12);This works for a small API surface; however, as the number of interfaces grows there is no compile‑time checking or IDE auto‑completion, increasing maintenance cost.
Option 2: Protobuf + gRPC (larger projects)
Define the API in .proto files to generate type‑safe Java and TypeScript code.
service PrefsService {
rpc SetFontSize(FontSize) returns (google.protobuf.Empty);
}
enum FontSize {
SMALL = 0;
DEFAULT = 1;
LARGE = 2;
}The Java side runs a gRPC server; 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);This approach provides type safety, automatic code generation, IDE completion, and compile‑time validation, which become increasingly valuable as the project scales.
References
GitHub repository with the full source code: https://github.com/TeamDev-IP/JxBrowser-Gallery
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Java Tech Enthusiast
Sharing computer programming language knowledge, focusing on Java fundamentals, data structures, related tools, Spring Cloud, IntelliJ IDEA... Book giveaways, red‑packet rewards and other perks await!
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.
