Skip to content
Happy Programming Guide
Start learning
Java

Java GUI Programming

Building desktop interfaces in Java: Swing for simplicity, JavaFX for modern applications, layout managers, events, and the threading rule you must not break.

Working desk of Ibrahim – Rajshahi, Bangladesh

Java has two mainstream toolkits for desktop interfaces: Swing, which ships with the JDK and is the quickest way to get a window on screen, and JavaFX, which is a separate dependency with better styling and a cleaner API. This guide covers both, the layout managers that decide where things go, and the single threading rule that causes most GUI bugs.

Which toolkit#

Swing JavaFX
Included in the JDK yes no, add a dependency
Styling look-and-feel classes CSS
Layout in markup no FXML
Animation and effects limited built in
Best for learning, internal tools new applications

Start with Swing if you want a window running in the next five minutes. Choose JavaFX for anything you intend to maintain.

A Swing window#

JAVA
import javax.swing.*;
import java.awt.*;

public class Counter {
    public static void main(String[] args) {
        SwingUtilities.invokeLater(Counter::createAndShow);
    }

    private static void createAndShow() {
        JFrame frame = new JFrame("Counter");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        JLabel label = new JLabel("0", SwingConstants.CENTER);
        label.setFont(label.getFont().deriveFont(48f));

        JButton button = new JButton("Add one");

        int[] count = {0};
        button.addActionListener(e -> {
            count[0]++;
            label.setText(String.valueOf(count[0]));
        });

        frame.setLayout(new BorderLayout(10, 10));
        frame.add(label, BorderLayout.CENTER);
        frame.add(button, BorderLayout.SOUTH);

        frame.setSize(320, 200);
        frame.setLocationRelativeTo(null);      // centre on screen
        frame.setVisible(true);
    }
}

The invokeLater call is not optional — see the threading section below. The array trick for count is a way around lambdas requiring effectively final variables; a field on a class is cleaner in real code.

The same in JavaFX#

XML
<dependency>
  <groupId>org.openjfx</groupId>
  <artifactId>javafx-controls</artifactId>
  <version>21</version>
</dependency>
JAVA
import javafx.application.Application;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

public class CounterApp extends Application {

    @Override
    public void start(Stage stage) {
        Label label = new Label("0");
        label.setStyle("-fx-font-size: 48px;");

        Button button = new Button("Add one");

        int[] count = {0};
        button.setOnAction(e -> label.setText(String.valueOf(++count[0])));

        VBox root = new VBox(15, label, button);
        root.setAlignment(Pos.CENTER);

        stage.setScene(new Scene(root, 320, 200));
        stage.setTitle("Counter");
        stage.show();
    }

    public static void main(String[] args) {
        launch(args);
    }
}

JavaFX styles with CSS, which is a large practical advantage if you have any web experience:

CSS
.button {
    -fx-background-color: #2563eb;
    -fx-text-fill: white;
    -fx-padding: 8 16;
    -fx-background-radius: 6;
}

Layout managers#

A layout manager decides where components go and how they resize. Positioning by pixel coordinates seems simpler and breaks the first time someone resizes the window or uses a different screen scaling.

Swing JavaFX Arranges
BorderLayout BorderPane five regions: centre plus four edges
FlowLayout FlowPane left to right, wrapping
GridLayout GridPane rows and columns
BoxLayout VBox, HBox a single row or column
GridBagLayout GridPane flexible grid, most control

Nesting is normal — a BorderPane holding a VBox holding an HBox is a perfectly ordinary structure.

The threading rule#

All interface updates must happen on one specific thread — the Event Dispatch Thread in Swing, the JavaFX Application Thread in JavaFX. Touching a component from any other thread produces intermittent, unreproducible bugs.

The corollary is that you must not do slow work on that thread either, because while it is busy the interface cannot repaint. That is what “the application has frozen” means.

JAVA
// Wrong - freezes the window for five seconds
button.addActionListener(e -> {
    String result = slowNetworkCall();
    label.setText(result);
});
JAVA
// Right - Swing
button.addActionListener(e -> {
    button.setEnabled(false);

    new SwingWorker<String, Void>() {
        @Override
        protected String doInBackground() {
            return slowNetworkCall();      // background thread
        }

        @Override
        protected void done() {
            try {
                label.setText(get());      // back on the EDT
            } catch (Exception ex) {
                label.setText("Failed: " + ex.getMessage());
            } finally {
                button.setEnabled(true);
            }
        }
    }.execute();
});
JAVA
// Right - JavaFX
Task<String> task = new Task<>() {
    @Override
    protected String call() {
        return slowNetworkCall();
    }
};

task.setOnSucceeded(e -> label.setText(task.getValue()));
task.setOnFailed(e -> label.setText("Failed"));

new Thread(task).start();

To update the interface from arbitrary background code, hand the work back to the right thread:

JAVA
SwingUtilities.invokeLater(() -> label.setText("done"));   // Swing
Platform.runLater(() -> label.setText("done"));            // JavaFX

Common controls#

JAVA
// Swing
JTextField field = new JTextField(20);
JCheckBox check = new JCheckBox("Remember me");
JComboBox<String> combo = new JComboBox<>(new String[]{"A", "B"});
JTable table = new JTable(data, columnNames);
JScrollPane scroll = new JScrollPane(table);

int answer = JOptionPane.showConfirmDialog(frame, "Delete this?");
JAVA
// JavaFX
TextField field = new TextField();
CheckBox check = new CheckBox("Remember me");
ComboBox<String> combo = new ComboBox<>();
TableView<Person> table = new TableView<>();

Alert alert = new Alert(Alert.AlertType.CONFIRMATION, "Delete this?");
alert.showAndWait();

JavaFX properties and binding#

JAVA
StringProperty name = new SimpleStringProperty("Ana");

Label label = new Label();
label.textProperty().bind(name);

name.set("Sam");        // the label updates on its own

This is JavaFX’s main advantage over Swing. Bind the interface to your data once and stop writing update code entirely — the same idea that makes web frameworks appealing.

Questions people ask#

Is Swing obsolete?

It is in maintenance rather than active development, but it still works, ships with the JDK, and there is a great deal of existing code using it. Fine for internal tools and for learning.

Why is JavaFX not in the JDK?

It was separated out from Java 11 so it could release on its own schedule. Add it as a dependency, or use a build of the JDK that bundles it.

Should I use FXML or build the interface in code?

FXML separates layout from logic and works with Scene Builder, a visual designer. Code is easier to refactor and keeps everything in one language. Both are common.

What about desktop applications in general?

For a cross-platform desktop application today, JavaFX is a reasonable choice, particularly if your team already knows Java. Electron and Tauri are the main alternatives if web skills are stronger.

Where to go next#

Concurrency, and why the interface thread must stay freeRead next

Keep reading

Java

Java Database Connectivity (JDBC)

Connecting to a database from Java with JDBC: prepared statements, try-with-resources, transactions, connection pooling, and when to use JPA instead.

4 min read

Java

Java Collections Framework

Lists, sets, maps and queues in Java: which to choose, how they differ in speed, and the mistakes that cause ConcurrentModificationException and…

4 min read

Java

Java Web Development

How a Java web application is put together: servlets, Spring Boot, REST endpoints, templates, database access and what to configure before deploying.

4 min read

Keep going — pick your next guide

The fastest way to improve is to read one guide, then build the thing it describes. Start with the basics, or jump straight to a project.

Ask a question or share what worked

Your email address will not be published. Required fields are marked *