Swing on Steroids: Modernizing Java Desktop Apps with FlatLaf and JReleaser
Java Swing has been around for decades, but that doesn’t mean your desktop apps need to look dated. With the right tools, you can create modern, stylish applications that support dark mode, high-DPI displays, and native installation packages. Let’s explore how to supercharge Swing development using FlatLaf for UI styling and JReleaser for distribution.
1. Creating Modern UIs with FlatLaf
FlatLaf is a open-source Look and Feel for Java Swing that brings your applications into the 21st century with:
- Flat design aesthetics – Clean, modern interfaces without the old-school 3D bevels
- Dark mode support – Automatic theme switching based on system preferences
- High-DPI scaling – Crisp rendering on 4K displays and retina screens
- Custom themes – Easy theming with JSON configuration files
Basic FlatLaf Setup
Getting started is simple. First, add the dependency to your project (Maven example):
<dependency>
<groupId>com.formdev</groupId>
<artifactId>flatlaf</artifactId>
<version>3.4.1</version>
</dependency>
Then set the Look and Feel at startup:
import com.formdev.flatlaf.FlatLightLaf;
public class MyApp {
public static void main(String[] args) {
FlatLightLaf.setup(); // Light theme
// OR for dark mode:
// FlatDarkLaf.setup();
// Your app code here
}
}
Automatic Dark/Light Mode Switching
Modern operating systems let users choose between light and dark modes. FlatLaf can respect this preference:
// Enable automatic dark/light theme switching
FlatLaf.registerCustomDefaultsSource("com.myapp.themes");
UIManager.put("defaultFont", new Font("Segoe UI", Font.PLAIN, 13));
FlatSystemProperties.setProperty("flatlaf.useWindowDecorations", "true");
// Track system dark mode changes
FlatLaf.setup(new FlatMacDarkLaf());
SystemThemeDetector.init("com.myapp.MyApp");
High-DPI Support
For crisp rendering on high-resolution displays:
// Enable automatic scaling based on system DPI settings
System.setProperty("sun.java2d.uiScale.enabled", "true");
System.setProperty("sun.java2d.uiScale", "1x");
System.setProperty("flatlaf.uiScale", "1x");
// Or force a specific scaling factor
// System.setProperty("flatlaf.uiScale", "1.5x");
Custom Themes
Create a JSON file in your resources (e.g., themes/mytheme.json):
{
"@base": "dark",
"background": "#282a36",
"foreground": "#f8f8f2",
"accent": "#bd93f9",
"selection.background": "#44475a"
}
Then load it:
FlatLaf.registerCustomDefaultsSource("themes");
FlatDarkLaf.setup();
2. Packaging as Native Installers with JReleaser
JReleaser is a tool that packages your Java application as native installers for various platforms:
- Windows: MSI installers
- macOS: Homebrew taps or DMG files
- Linux: DEB/RPM packages
- Cross-platform: ZIP archives with native launchers
Basic JReleaser Configuration
Create a jreleaser.yml file in your project:
project:
name: myapp
version: 1.0.0
description: A modern Swing application
authors:
- Your Name <you@example.com>
distributions:
app:
artifacts:
- path: target/myapp-${project.version}.jar
java:
mainClass: com.myapp.Main
jlink:
enabled: true
nativeImage:
enabled: false
release:
github:
owner: yourusername
name: myapp
Platform-Specific Packaging
Windows MSI Installer:
assemblers:
jpackage:
- name: windows
jpackage:
imageName: MyApp
imageOptions: ["--win-menu", "--win-shortcut"]
formats: ["msi"]
platform: windows
macOS Homebrew Tap:
brew:
tap:
owner: yourusername
name: homebrew-tap
formulae:
- name: myapp
install: |
bin.install "myapp"
Linux DEB Package:
assemblers:
jpackage:
- name: linux
jpackage:
imageName: myapp
formats: ["deb"]
platform: linux
Building and Releasing
Run JReleaser with:
# Dry run (test configuration) jreleaser assemble --dry-run # Full release jreleaser release
3. Putting It All Together: A Complete Example
Let’s create a simple dark-mode Swing app with native packaging.
1. Application Code (Main.java):
import com.formdev.flatlaf.FlatDarkLaf;
import javax.swing.*;
public class Main {
public static void main(String[] args) {
FlatDarkLaf.setup();
JFrame frame = new JFrame("Modern Swing App");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(800, 600);
JButton button = new JButton("Click Me");
JLabel label = new JLabel("Hello, Modern Java!");
JPanel panel = new JPanel();
panel.add(label);
panel.add(button);
frame.add(panel);
frame.setVisible(true);
}
}
2. Maven pom.xml:
<project>
<!-- ... other config ... -->
<dependencies>
<dependency>
<groupId>com.formdev</groupId>
<artifactId>flatlaf</artifactId>
<version>3.4.1</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.5.0</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.jreleaser</groupId>
<artifactId>jreleaser-maven-plugin</artifactId>
<version>1.8.0</version>
</plugin>
</plugins>
</build>
</project>
3. Build and Release:
# Build the JAR mvn package # Create native packages mvn jreleaser:assemble mvn jreleaser:release
4. Advanced Tips
- Custom Icons – Replace standard Swing icons with SVG-based ones for perfect scaling on any display. Libraries like SVG Salamander can help render them in Swing.
- Animation – Use TimingFramework or Trident to add smooth transitions (e.g., fade-ins, sliding panels) without blocking the Event Dispatch Thread (EDT).
- Native Integration – JNA (Java Native Access) allows calling OS-specific APIs (e.g., Windows taskbar progress, macOS dock badges).
- Auto-Updates – Update4j enables self-updating apps by checking for new versions and applying patches without user intervention.
- App Stores – JPackage’s
--mac-app-storeflag helps package apps for distribution on the Mac App Store with sandboxing compliance.
Advanced Tips Summary (Table Format)
| Tip | Key Tools/Libraries | Use Case | Example Benefit |
|---|---|---|---|
| Custom Icons | SVG Salamander, FlatLaf Icons | High-DPI displays, modern UI | Crisp icons at any resolution |
| Animation | TimingFramework, Trident | Smooth transitions, loading indicators | Better UX with fluid motions |
| Native Integration | JNA, JNI | OS-specific features (notifications, badges) | Windows taskbar progress bars |
| Auto-Updates | Update4j, JReleaser | Seamless app updates | Users always get the latest version |
| App Stores | JPackage (--mac-app-store) | Distributing via Mac App Store | Monetization & wider reach |
When to Use Each
- For UI polish → Custom Icons + Animation
- For OS features → Native Integration (JNA)
- For maintenance → Auto-Updates (Update4j)
- For distribution → JPackage + JReleaser
5. Resources to Explore Further
- FlatLaf Demo Application – See all components in action
- JReleaser Documentation – Detailed packaging options
- Java Desktop Best Practices – Modern Java UI patterns
- SwingX – Extended Swing components
With these tools, your Swing applications can look and feel like modern native apps while maintaining Java’s cross-platform advantages. The combination of FlatLaf for styling and JReleaser for distribution removes the traditional pain points of Java desktop development.

