Java has a powerful solution for developing rich client applications — JavaFX. Introduced in 2008, it currently matures within the OpenJFX project delivering excellent cross-platform compatibility and modern out-of-the-box solutions for UI. With JavaFX, you can write familiar code in Java to develop desktop applications, without resorting to the complexities of JavaScript or .NET.
In this article, we will explore JavaFX and its features and learn how to write, compile, and deploy JavaFX-based applications.
Table of Contents
What is JavaFX?
JavaFX is an open-source platform containing graphics and media tools for developing and deploying rich client applications — programs that store and retrieve data and perform most operations locally, i.e., on a client machine. JavaFX apps run consistently in different environments: desktop, web, mobile, and embedded systems. They can also reference APIs from other Java libraries to use all capabilities of the language and connect to server apps.
Why use JavaFX?
Desktop applications are not a dying technology. There are numerous cases when local/native applications are a preferred choice for users as they provide a more advanced GUI experience, higher performance, and reliability. Use cases for desktop applications include:
- IDEs
- Editors
- Audio and video editors/players
- Games
In addition, some web and mobile applications have their desktop counterparts, such as Skype, WhatsApp, Telegram, and Slack.
So, desktop development is still a lucrative field for developers, which makes JavaFX a technology worth learning. As per TheirStack data, at least 1,087 companies whose data on the used software stack is available utilize JavaFX (as of February 2025).
Furthermore, JavaFX goes beyond pure desktop development. For instance, JavaFX apps can be built as native images and used on mobile devices: Gluon offers tools for that purpose, and Liberica Native Image Kit can be used for the fast and convenient transformation of Java apps into native executables.
JavaFX also goes well with embedded devices such as Raspberry Pi. Refer to our guide on running Liberica JDK with JavaFX on this popular single-board computer.
JavaFX vs Swing: key differences
You might be wondering at that point, “If a JDK already contains Swing, why should I bother with JavaFX?”
As mentioned, JavaFX was designed as a substitution for Swing, which lacks many modern features. As a result, it offers many more opportunities for desktop development than Swing. Here is a short comparison of both technologies:
JavaFX |
Swing |
Used for developing rich client applications with moden user interface |
Legacy library for GUI development |
Cleaner code base |
Many legacy features |
Integrated support for MVC |
Inconsistent support for MVC |
Evolves within the community that regularly introduces enhancements and new features |
No new functions are added |
Supports both CSS and code-based styling |
Only code-based styling |
Integrated API for concurrency |
No built-in API for concurrency |
FXML for declarative layout |
No support for declarative layout |
Built-in 3D graphics support |
Requires additional API for 3D |
Support for property binding |
No property binding |
Comes as a separate bundle starting with JDK 11 |
Included into JDK by default |
Comparative table for Swing and JavaFX
Let’s compare two demo apps written with JavaFX and Swing, which illustrate button customization.
Customizing a button with JavaFX:
public class ScaleTransformationDemo extends Application {
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage primaryStage) throws Exception {
Button button = new Button();
button.setText("Click me!");
Scale scaleTransformation = new Scale();
scaleTransformation.setX(3.0);
scaleTransformation.setY(2.0);
scaleTransformation.setPivotX(0);
scaleTransformation.setPivotY(0);
button.getTransforms().add(scaleTransformation);
VBox vbox = new VBox(button);
Scene scene = new Scene(vbox);
primaryStage.setScene(scene);
primaryStage.setWidth(512);
primaryStage.setHeight(256);
primaryStage.show();
}
}
In the example above taken from the JavaFX Button tutorial, we added a scale transformation. The customization process with Swing is different.
Customizing a button with Swing:
public class ButtonCustomization extends BasicButtonUI {
public static void main(String[] args) {
JFrame f = new JFrame("Button UI Test");
f.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
JPanel p = new JPanel();
p.setBackground(Color.white);
f.setContentPane(p);
p.setLayout(new FlowLayout(5, 5, 5));
p.setBorder(new EmptyBorder(10, 10, 10, 10));
final JButton button = new JButton("Click me!");
button.setFont(new Font("Calibri", Font.PLAIN, 14));
button.setBackground(new Color(0x2dce98));
button.setForeground(Color.white);
button.setUI(new ButtonCustomization());
p.add(button);
f.pack();
f.setLocation(500, 500);
f.setVisible(true);
}
@Override
public void installUI(JComponent c) {
super.installUI(c);
AbstractButton button = (AbstractButton) c;
button.setOpaque(false);
button.setBorder(new EmptyBorder(5, 15, 5, 15));
}
@Override
public void paint(Graphics g, JComponent c) {
AbstractButton b = (AbstractButton) c;
paintBackground(g, b, b.getModel().isPressed() ? 2 : 0);
super.paint(g, c);
}
private void paintBackground(Graphics g, JComponent c, int yOffset) {
Dimension size = c.getSize();
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g.setColor(c.getBackground().darker());
g.fillRoundRect(0, yOffset, size.width, size.height - yOffset, 10, 10);
g.setColor(c.getBackground());
g.fillRoundRect(0, yOffset, size.width, size.height + yOffset - 5, 10, 10);
}
}
To avoid the complexity of code-based styling with Swing, try using CSS supported by JavaFX. Refer to the official JavaFX CSS Reference Guide for details.
Now, if you want to animate the scaling of the JavaFX button, you simply need to use ScaleTransition. The same task won’t be as easy for Swing because it neither has a dedicated class nor concurrency support.
History and Evolution of JavaFX
History
Graphical components have been present in Java since the early age of the language. At first, the focus was made on applets, small applications deployed in web browsers. But as the demand for full-blown desktop apps grew, Java evolved accordingly. First Java versions contained only AWT (Abstract Window Toolkit), a low-level abstract graphic API. Swing APIs introduced in 1998 were built on top of AWT and were aimed at extending the AWT functionality.
JavaFX was released as part of the JDK in 2008. The first version of JavaFX was a scripting language built on top of the JVM. Version 2 of JavaFX already came as a set of Java libraries.
Starting with Java 11, JavaFX was separated from JDK and now evolves within the OpenJFX project.
Support roadmap
Oracle Java 8 still includes JavaFX, which will be supported until March 2025. The release schedule of OpenJFX is based on quarterly updates with security patches and bug fixes. The community provides LTS and minor versions and commercial support by Gluon upon request. BellSoft bundles JavaFX with its own OpenJDK distribution, Liberica JDK (see below).
JavaFX Release |
GA date |
Latest version |
Long-term support |
Java 11 |
September 2018 |
11.0.26 (January 2025) |
Yes |
Java 17 |
September 2021 |
17.0.14 (January 2025) |
Yes |
Java 21 |
September 2023 |
21.0.6 (January 2025) |
Yes |
Java 23 (current) |
September 2024 |
23.0.2 (January 2025) |
No |
Java 24 |
March 2025 (planned) |
Early access |
No |
Java 25 |
September 2025 (planned) |
Early access |
Yes |
JavaFX support roadmap
JavaFX comes as a platform-specific SDK, jmods, or a set of artifacts. If you want to use the software with your Java runtime, download the necessary bundle from the developer’s website. We deliver a special flavor of Liberica JDK (Full version) containing LibericaFX — our instance of OpenJFX. Liberica for Embedded also comes with LibericaFX enabling the developers to create GUIs for embedded systems.
JavaFX integrated into the runtime is very convenient:
- Minimize time and effort spent on installing JavaFX as a separate plugin;
- Get timely fixes together with runtime updates;
- Receive first-hand support from our engineers within the scope of JDK support.
End of Life for JavaFX support on Oracle Java SE 8
Support for JavaFX on Java SE 8 ends in March 2025 as per Oracle Support Roadmap. It means that Oracle will no longer provide Oracle Java 8 builds with JavaFX. If you have been using Oracle Java SE 8 with JavaFX, you will have to either upgrade to a newer JDK version and request support for JavaFX or migrate to Liberica JDK 8, which enjoys extended long-term support until March 2032, including JavaFX support. You can get and use Liberica JDK 8 Full with Java FX at no cost. Or from SDKMAN:
sdk install 8.0.432.fx-librca
Key advantages of JavaFX
There are several reasons to use JavaFX for developing GUI applications:
- JavaFX enables the developers to write clear, manageable code in Java, which is easy to update or debug. Writing code for desktop applications in JavaScript is far more complicated, and cross-platform compatibility of .NET apps is inconsistent.
- There is a short learning curve for Java developers or programmers who have already worked with Java-like technologies.
- JavaFX includes all necessary libraries for desktop development and supports CSS styling, FXML, and multithreading. It provides many functions out-of-the-box (see the section below), and as a bonus, it has a convenient JavaFX Scene Builder, a visual layout tool for designing UIs without coding.
- JavaFX is open source and part of an active community. Updates with security patches and bug fixes come out regularly, and even when Oracle ceases JavaFX support as part of Java 8 in 2025, the technology will still be developed by the community within the OpenJFX project.
- Thanks to the support for native image technology in Liberica NIK 21.3 and up, the JavaFX app can be bundled and deployed as native executables that take less space on a disk and start up almost instantly.
How to install JavaFX
As JavaFX evolves as a standalone project, there are two ways of getting it: you can download JavaFX separately from the OpenJFX website or get JDK builds bundled with JavaFX. BellSoft is one of few OpenJDK vendors that provide JDK binaries with JavaFX. You can get Liberica JDK Full version, which is a version with JavaFX, from Liberica JDK Download Center, or get Liberica JDK with JavaFX via a package manager. For instance, in SDKMAN they have an 'fx' tag. So, for instance, to get Liberica JDK 23 with JavaFX, run
sdk install 23.fx-librca
JavaFX architecture and core components
JavaFX components
JavaFX has an extensive range of features for developing GUIs: tables, buttons, trees, menus, and many more. It also supports CSS, 2D and 3D Graphics, and WebView.
Key JavaFX components:
- Stage is a window in a JavaFX application. When the application starts, it creates a root Stage object, which is the main window of the app. But you can create multiple stages if your app has multiple windows.
- Scene is a component where you add GUI components. A Scene object is set on the Stage.
- Node is a superclass of all components added to the Scene Graph. Stage and Scene make the Scene Graph visible, but only Node elements added to the Scene are considered part of the Stage Graph.
- FXML is an XML-based markup language for creating a layout of JavaFX apps. FXML helps you to separate the layout code from the application code, making the latter more concise.
JavaFX features with examples of subcomponents for better understanding are listed below:
- Core: FXML, Stage, Scene, etc.
- Layout: HBox, VBox, Border Pane, Text Flow, Flow Panel, etc.
- UI controls: Label, Button, TextField, MenuBar, etc.
- Container controls: Accordion, TablePane, etc.
- Web: WebView, etc.
- Charts: PieChart, BarChart, etc.
- Other tools: fonts, animation, effects, etc.
JavaFX APIs
JavaFX public APIs include all classes required for building a GUI application. The most important ones are listed below:
- javafx.scene provides a set of core classes for the JavaFX Scene Graph API, which is the foundation for JavaFX applications. There are several subpackages with classes for different purposes such as working with texts, images, media, keyboard and mouse input events handling, and many more.
- javafx.animation includes classes for adding and handling transition-based animations.
- javafx.css provides classes for working with styles using CSS.
- javafx.fxml includes classes for loading objects from the markup file.
- javafx.event provides classes for handling JavaFX events.
- javafx.geometry contains classes for working with 2D objects.
- javafx.stage provides container classes for the JavaFX content.
JavaFX tools and libraries
Numerous libraries, frameworks, and third-party resources enhance the experience of working with JavaFX. The libraries enable the developers to create beautiful apps with more concise code, and frameworks add extra functionality.
Let’s discuss some of them briefly to illustrate the capabilities of the JavaFX platform you can make use of.
- MigLayout is an open-source library that aids in developing and managing layouts. It provides tools for multiple layout types: flowing, grip-based, docking, etc. The code written in MigLayout is very concise and represents the appearance of a layout clearly.
- Ikonli is a library providing numerous icon packs for different icons and making it possible to customize and style icons.
- RichTextFX offers tools for creating rich text editors and code editors with syntax highlighting and various fonts.
- JacpFX is a framework that helps to structure the app with loosely coupled, reusable JavaFX components. The task execution can be separated from UI changes in the client application, thus enabling the developers to avoid the multithreading issues. JavaFX functionalities include message-bus communication between components and asynchronous processes support.
- Skija provides Java bindings for Skia, an open-source library for developing rich 2D graphics. It is highly performant, easy to use, and robust, with support for color spaces, modern typography and GPU backends, and highly-optimized GPU rendering.
Creating a user interface with JavaFX
Scene Builder
The easiest way to start building a UI with JavaFX is to use a Scene Builder. It has a convenient drag-and-drop interface where you can create a layout for your application and export it as an FXML file.
Download, install, and open the application.
Scene Builder interface
On the left, you can see the Library with the groups of UI components: containers with panes and boxes, controls with buttons, text, labels, etc, and so on. In the middle on the left, there is a Document section showing the hierarchy of elements we will add to the Scene Graph, and at the bottom, there is a Controller where you can specify the Java Controller class for this window.
On the right, there is the Inspector where you can customize each element.
Layout
Let’s start with a layout. Some important containers:
- HBox and VBox are the components that place all their child nodes in a horizontal or a vertical row accordingly.
- BorderPane is a pane that arranges the child nodes in the top, left, right, bottom and center positions.
- GridPane places the child nodes in a grid.
- FlowPane lays out the child nodes vertically or horizontally and can wrap them at their width or height accordingly.
- TilePane lays out the child nodes in a grid with equally sized cells.
Let’s use BorderPane. Choose the component in the Containers section and drag it to the center of the canvas. This is going to be our starting point. You can customize the element. On the right, there are three sections: Properties, Layout, and Code.
We will change the background color. Select Properties, Style, and paste -fx-background-color to the left cell and #E0F6FD to the right. Also, go to Layout and set Padding on the bottom to 10.
Main UI components
There are numerous UI components you can add to a JavaFX application, including:
- Text elements: Label, TextArea, TextField;
- Buttons: Button, CheckBox, RadioButton;
- Charts and diagrams: PieChart, BarChart;
- Sliders and bars: MenuBar, ScrollBar, Slider.
In Controls, choose TextArea and drag it to the top of the BorderPane. Go to Code and enter text in fx:id.
Choose Label in Controls and drag it to the center of the pane. Go to Code and enter dataLabel in fx:id.
Also in Controls, choose Button and drag it to the bottom of the pane. Go to Properties and name the button Say hello!
You can play around with colors, fonts, etc. After that, go to Code and enter helloButton in fx:id and sayHello in onAction. These fields are required to tie the FXML file to the code of our application.
This is the resulting window.
Finally, in the Controller section at the bottom, enter the name of the Controller you will use in your app. In my case, it was dev.cat.HelloController.
Save the file and let’s move on to the IDE.
Create a JavaFX project
The easiest way to create a JavaFX project is to install an OpenJDK distribution bundled with JavaFX. In this case, you can create a project without adding additional dependencies for FX.
Download Liberica JDK Full, which includes LibericaFX, an instance of OpenJFX. You can also get the binaries from your favorite package manager. For instance, with SDKMAN:
sdk install 23.fx-librca
Install Liberica JDK, then create a new Java project in your IDE.
Creating JavaFX project
Integrate FXML file in your JavaFX application
Place the .fxml file into the resources directory.
First of all, we need to make our Main application class extend Application from the javafx.application package, implement its method start()
, and call the method launch()
from the main method:
public class Main extends Application {
@Override
public void start(Stage primaryStage) throws Exception {
}
public static void main(String[] args) {
launch(args);
}
}
Now, we need to load the FXML file in the start()
method. For that purpose, create an instance of FXMLLoader and set the location of the FXML file using its setLocation()
method.
FXMLLoader loader = new FXMLLoader();
loader.setLocation(new URI("file:src/main/resources/home.fxml").toURL());
After that, create a root node and load the file:
BorderPane pane = loader.load();
Finally, create a Scene, passing the root node as the parameter, and set the Scene on the Stage. The whole body of the method should look like this:
@Override
public void start(Stage primaryStage) throws Exception {
FXMLLoader loader = new FXMLLoader();
loader.setLocation(new URL("file:src/main/resources/home.fxml"));
BorderPane pane = loader.load();
Scene scene = new Scene(pane);
primaryStage.setScene(scene);
primaryStage.show();
}
It’s time to connect the FXML file to the Controller. Create a new Java class, for instance, HelloController. FXML files can be connected to Java fields and methods using the @FXML
annotation.
So, as we tied the button to the sayHello method, let’s create it in the Controller and annotate this method with @FXML
. In addition, we need two fields, TextArea and Label, also annotated with @FXML
. Make sure that the names of the variables are the same as fx:id field in the FXML file.
public class HelloController implements Initializable {
@FXML
private TextArea text;
@FXML
private Label dataLabel;
@FXML
void sayHello() {
}
@Override
public void initialize(URL location, ResourceBundle resources) {
}
Specify the name of the Controller in the FXML file if you haven’t done it in Scene Builder:
<BorderPane xmlns="http://javafx.com/javafx/23.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="dev.cat.HelloController">
The FXML file is now tied to our application. But the app doesn’t do anything useful yet, let’s fix that.
Change the text of the Label using StringProperty
It is possible to set a text or even an image of a Label that will be displayed when you run the application. For our simple scenario, we could simply use the setText()
method of a Label class.
However, we will implement another approach using the StringProperty, whose values can be observed or changed and displayed dynamically based on the user input.
For that purpose, we will need to add and initialize the StringProperty field in the Controller:
StringProperty name = new SimpleStringProperty();
In the initialize()
method, we need to bind the value of Label to StringProperty:
@Override
public void initialize(URL location, ResourceBundle resources) {
dataLabel.textProperty().bind(name);
}
Finally, in the sayHello()
method, use the setMethod()
of StringProperty to change the value of name to user input that we can extract from TextArea with getText()
:
@FXML
void sayHello() {
name.setValue("Hello, " + text.getText() + "!");
}
The whole body of the Controller class:
public class HelloController implements Initializable {
@FXML
private TextArea text;
@FXML
private Label dataLabel;
StringProperty name = new SimpleStringProperty();
@FXML
void sayHello() {
name.setValue("Hello, " + text.getText() + "!");
}
@Override
public void initialize(URL location, ResourceBundle resources) {
dataLabel.textProperty().bind(name);
}
You can now run the application and check that everything works correctly.
How to switch scenes in JavaFX
Our tiny application works with one scene, but in some cases, it won’t be enough. So, how do we switch scenes?
First of all, you need to create a new FXML file and add it to the project. Then, you create a Controller class for this file and connect the business logic to the interface like we did before.
There are several ways of switching scenes. Here’s one of them for the scenario when you need to change the scene as soon as the user pushes the button.
First, create a static Stage field in the Main class and tie it to the primaryStage in the start()
method:
private static Stage stage;
@Override
public void start(Stage primaryStage) throws Exception {
stage = primaryStage;
FXMLLoader loader = new FXMLLoader();
loader.setLocation(new URI("file:src/main/resources/home.fxml").toURL());
BorderPane pane = loader.load();
Scene scene = new Scene(pane);
primaryStage.setScene(scene);
primaryStage.show();
}
Second, create a static method switchToSecondScene()
in the Main class. You can use any sensible name for the method, this one here is just an example. In this method, create an instance of FXMLLoader and set the location of the second FXML file. Then, create a new Scene and add it to the Stage:
public static void switchToSecondScene() {
FXMLLoader loader = new FXMLLoader();
loader.setLocation(new URI("file:src/main/resources/second-view.fxml").toURL());
BorderPane pane = loader.load();
Scene sceneTwo = new Scene(pane);
stage.setScene(sceneTwo);
stage.show();
}
Finally, call the switchToSecondScene()
method in the Controller connected to the first FXML file. If the scene changes upon button action, add the call to the button handling method:
@FXML
void switchScene() throws Exception {
Main.switchToSecondScene();
}
You now have two scenes in your JavaFX application, but what if you need to pass data between two Controllers?
You can accomplish this task in four steps:
Add a parameter to the switchToSecondScene()
method that specifies the data you want to pass. For instance, a String:
public static void switchToSecondScene(String text) { }
Extract the data in the first Controller and pass it to the method:
@FXML
private TextArea text;
@FXML
void switchScene() throws Exception {
Main.switchToSecondScene(text.getText());
}
Define a method for saving the data in the second Controller:
public void saveData(String text) {
//saving logic
}
Get an instance of the second Controller in the switchToSecondScene()
method by calling loader.getController()
and call the Controller method for saving the data:
public static void switchToSecondScene(String text) {
FXMLLoader loader = new FXMLLoader();
loader.setLocation(new URI("file:src/main/resources/second-view.fxml").toURL());
BorderPane pane = loader.load();
SecondController controller = loader.getController();
controller.saveData(text);
Scene sceneTwo = new Scene(pane);
stage.setScene(sceneTwo);
stage.show();
}
Animations in JavavFX
Animation is the process of changing the position of objects or applying transformations to create an illusion of movement.
We can add animations to JavaFX applications. The javafx.animation package includes classes for applying various transitions to the objects. These transitions can be roughly classified as
- Simple transitions represented by FadeTransition, ScaleTransition, RotateTransition:
- The FadeTransition class is used to change the opacity of the JavaFX node over time.
- ScaleTransition enables scaling a node over time.
- RotateTransition enables rotating a node by a specified axis over time.
- Complex transitions represented by SequentialTransition and ParallelTransition:
- SequentialTransition plays a list of animations in sequential order.
- ParallelTransition plays a list of animations in parallel.
- Animation by trajectory is made possible with PathTransition, which moves a node over specified path over time.
- Custom transitions can be created using the Timeline class. You can add KeyFrames to the timeline specifying the animation and its duration.
Take a look at an example of animating an object in JavaFX. Let’s return to our minimalistic application we created above. Suppose we want to animate a Button and add ScaleTransition to enlarge the button on mouse hover.
Add the button field and annotate it with @FXML
. In the initialize()
method, create a ScaleTransition instance:
public class HelloController implements Initializable {
@FXML
private Button helloButton;
@Override
public void initialize(URL location, ResourceBundle resources) {
ScaleTransition scale = new ScaleTransition(Duration.seconds(1), helloButton);
scale.setByX(0.5);
scale.setByY(0.5);
scale.setCycleCount(2);
scale.setAutoReverse(true);
helloButton.setOnMouseEntered(e -> scale.play());
}
The code in the initialize()
method
- Creates a new ScaleTransition, sets the duration and the object we want to apply this transition to;
- Sets the new size of a button with
setByX()
andsetByY()
; - Returns the button to the normal size after playing the animation with
setAutoReverse(true)
; - Defines when the animation will be played, in our case, on mouse hover with
setOnMouseEntered()
.
Event handling in JavaFX
The application needs to react to the user's actions. In JavaFX, when the user interacts with the application nodes, for instance, pushes a button, an event is triggered. This event is handled by the application code.
The javafx.event package includes classes that can be used to handle various events. Some of the key JavaFX events are:
- MouseEvent (MouseClicked, MouseEntered, MouseExited) is an event triggered when the mouse is used.
- KeyEvent (KeyPressed, KeyReleased, KeyTyped) is an event triggered when the keystroke is detected on a node.
- WindowEvent is an event triggered when window-related actions happen like showing or hiding a window.
- DragEvent is an event triggered when the user drags and drops items in the interface.
In the example above, when we added action to our button, we used an event onMouseEntered meaning that when the user hovers a mouse over a button, an animation is played.
helloButton.setOnMouseEntered(e -> scale.play());
You can also use the EventFilter when you need to evaluate the event before it is processed. For instance, we can disable exiting the application with the ESCAPE key:
scene.addEventFilter(KeyEvent.KEY_PRESSED, event -> {
if (event.getCode() == KeyCode.ESCAPE) {
System.out.println("Escape key disabled");
event.consume();
}
});
You can also create custom events by extending the Event class:
public class CustomEvent extends Event {
public CustomEvent(EventType<? extends Event> eventType) {
super(eventType);
}
//business logic here
}
How to compile and deploy JavaFX applications
There are two ways of building JavaFX applications. You can create a standard JAR file or a native image.
To build a JAR file, add the following plugin to your pom.xml file:
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<manifest>
<mainClass>dev.cat.LotteryApp</mainClass>
</manifest>
</archive>
</configuration>
</plugin>
</plugins>
</build>
You may also have to specify the start class in the properties section:
<properties>
<maven.compiler.source>23</maven.compiler.source>
<maven.compiler.target>23</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<start-class>com.fxdemo.Main</start-class>
</properties>
After that, you can create a JAR file with
mvn clean package
JavaFX applications can also be turned into native images that start up almost instantly. But these builds are OS and platform-specific, meaning that if you compile the app on AArch64 mac, it won’t run on x86_64 Linux. The solution is to build the native image further down the CI/CD pipeline, for instance, using GitHub Actions.
Refer to these guides on building JavaFX native images and using GraalVM Native Image with GitHub Actions for more information.
Conclusion
JavaFX is still commonly used in enterprise development. This technology is actively developed by the OpenJFX community, and enterprise support is provided by several companies, including BellSoft.
Starting with JavaFX is no challenge, and FXML integration makes desktop development with Java even more convenient.
For more guides on JavaFX, refer to the official OpenJFX documentation.