Package a JavaFX Application as a Platform Specific Executable

JavaFX is a software platform that runs on top of a JDK and enables you to develop desktop applications, as well as rich web applications running across a broad range of devices. When your JavaFX application is ready, you can package it into a self-executable-jar and release it to your users. But this means that your users should have installed the same JDK version that you used when packaging your application. To avoid this situation, you can create a custom runtime image containing only the platform modules required by your application.

Compile a JavaFX application with jmods

Let’s consider the following application that changes the color of the circle whenever is clicked:

package helloworld;

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.control.Button;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;

import javafx.scene.Group;
import javafx.scene.input.MouseEvent;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;

import javafx.scene.text.Font;
import javafx.scene.text.FontWeight;
import javafx.scene.text.Text;

public class HelloWorldFX extends Application {

	public void start(Stage stage) {
		String javaVersion = System.getProperty("java.version");
		String javafxVersion = System.getProperty("javafx.version");
		Circle circle = new Circle(300.0f, 200.0f, 50.0f);

		//Set text
		Text text = new Text("Click on the circle to change its color");
		text.setFont(Font.font(null, FontWeight.BOLD, 20));

		//Setup the mouse event handler 
		EventHandler<MouseEvent> eventHandler = new EventHandler<MouseEvent>() {
			public void handle(MouseEvent e) {
				System.out.println(String.format("Hello World running on JavaFX %s on top of Java %s", javafxVersion, javaVersion));
				if (circle.getFill().equals(Color.BLUE)) 
		//Register the event filter 
		circle.addEventFilter(MouseEvent.MOUSE_CLICKED, eventHandler);

		//Add circle and text to a group
		Group root = new Group(circle, text);
		Scene scene = new Scene(root, 640, 480);

		stage.setTitle("Color shifter");

	public static void main(String[] args) {


The following file defines the helloworldfx module:

module helloworldfx {
    requires javafx.controls;
    exports helloworld;

You can run your JavaFX application using the set of jmod files available in the JavaFX release page. Download the archive appropriate for your operating system:

  • Linux/x64
  • macOS/x64
  • macOS/AArch64
  • Windows/x64

You can try the example application with JavaFX 21 or newer. In case your operating system is Linux/x64 or macOS, unpack the downloaded archive and add the environment variable pointing to the resulting jmods directory:

export PATH_TO_FX_JMODS=path/to/javafx-jmods-21.0.1

If you are a user of Windows operating system, unzip the downloaded archive and add the environment variable pointing to the resulting jmods directory:

set PATH_TO_FX_JMODS="path\to\javafx-jmods-21.0.1"

Let’s compile the application using JavaFX jmods:

  • for Linux/x64, macOS/x64, macOS/AArch64
javac --module-path $PATH_TO_FX_JMODS -d mods/helloworldfx $(find . -name "*.java")
  • for Windows
dir /s /b *.java > sources.txt & javac --module-path %PATH_TO_FX_JMODS% -d mods/helloworldfx @sources.txt & del sources.txt

Ship a self-contained JavaFX application as a platform specific executable

Because helloworldfx is a modular application, you can use jpackage to automatically generate a runtime image. In case your local operating system is macOS, you can run the following command to build a native package in a dmg format:

jpackage \
 --dest output \
 --name ShiftingCircle \
 --type dmg \
 --module-path $PATH_TO_FX_JMODS:mods \
 --add-modules helloworldfx \
 --module helloworldfx/helloworld.HelloWorldFX \
 --mac-package-name ShiftingCircle \
 --mac-package-identifier helloworld.HelloWorldFX \
 --java-options -Xmx2048m

jpackage will automatically run jlink and generate a runtime with the modules needed.

The previous command builds a native macOS application (--type dmg) in the output folder (--dest output) with the name ShiftingCircle. To create this runtime image you should specify the path to JavaFX jmods (--module-path $PATH_TO_FX_JMODS:mods), but also add the module containing (--add-modules helloworldfx).

The --module helloworldfx/helloworld.HelloWorldFX option instructs the application launcher where to find the main class of the application. As the resulting executable is a native macOS image, you can add a few Mac-specific options:

  • define the name of the application as it appears in the Menu Bar (--mac-package-name ShiftingCircle)
  • when signing the application package, components that don’t have an existing package identifier will be prefixed by the value of --mac-package-identifier option.

If you are running on Windows operating system, you can ship the previous application as a Microsoft Installer (msi) file. Prior to running the jpackage command, make sure that you have installed WiX tools. Then use the following command in a Command Prompt window:

 --dest output 
 --name ShiftingCircle 
 --type msi 
 --module-path "%PATH_TO_FX_JMODS%;mods" 
 --add-modules helloworldfx 
 --module helloworldfx/helloworld.HelloWorldFX 
 --win-shortcut --win-menu 
 --java-options -Xmx2048m

Running the previous command will create a ShiftingCircle-1.0.msi Microsoft Installer file. Running ShiftingCircle-1.0.msi will request to add a Start Menu shortcut for the application and to create a desktop shortcut associated to it.

Shifting Circle Color Application

In conclusion, if you want to ship Java/JavaFX applications without requiring users to install a JDK, consider using jpackage to create native macOS, Windows, or Linux software.