JavaFX Fundamentals & the Scene Graph

Your First JavaFX Window

18 min Lesson 5 of 12

Your First JavaFX Window

The previous lessons introduced the Application class, the lifecycle, and the tree of nodes that makes up a scene. Now you put it all together into a real, working window that a user can interact with. By the end of this lesson you will have a JavaFX application that displays a button and responds when the user clicks it — the smallest meaningful GUI program you can write.

What the Runtime Needs from You

JavaFX requires exactly one class to extend javafx.application.Application and override start(Stage primaryStage). That method is your entry point into the GUI world. Everything that touches the scene graph must happen here, or on the JavaFX Application Thread (covered fully in the next lesson). The main method simply calls launch(args) to hand control to the toolkit.

On Java 11+ you can omit main entirely when the module system is configured — but keeping it is safer and more portable across build tools.

Building the Scene Step by Step

A JavaFX window is constructed bottom-up: you create the controls first, assemble them into a layout container, wrap that in a Scene, and place the scene onto the Stage. Here is the full program:

import javafx.application.Application; import javafx.scene.Scene; import javafx.scene.control.Button; import javafx.scene.control.Label; import javafx.scene.layout.VBox; import javafx.stage.Stage; public class HelloWindow extends Application { @Override public void start(Stage primaryStage) { // 1. Create the controls Label label = new Label("Click the button below"); Button button = new Button("Say Hello"); // 2. Wire up the event handler (lambda — covered in lesson 8) button.setOnAction(event -> label.setText("Hello, JavaFX!")); // 3. Arrange controls in a vertical layout container VBox root = new VBox(12); // 12px gap between children root.setStyle("-fx-padding: 24; -fx-alignment: center;"); root.getChildren().addAll(label, button); // 4. Wrap the layout in a Scene (width=320, height=200) Scene scene = new Scene(root, 320, 200); // 5. Configure and show the Stage primaryStage.setTitle("Hello JavaFX"); primaryStage.setScene(scene); primaryStage.show(); } public static void main(String[] args) { launch(args); } }

Let's walk through every decision in that 30-line program.

Step 1 — Controls Are Just Objects

Label and Button are both subclasses of Node. You construct them with new, set properties with setters, and add them to a parent container. There is no XML, no descriptor file, and no IDE wizard required — it is plain Java object construction.

Step 2 — Responding to an Event with a Lambda

The line button.setOnAction(event -> label.setText("Hello, JavaFX!")) is the core of GUI programming: connect a user action to a piece of code. setOnAction accepts any EventHandler<ActionEvent>. Because that interface is functional (single abstract method), a lambda works perfectly. When the button is clicked, the toolkit calls this lambda on the JavaFX Application Thread, which is also the thread that owns the scene graph — so updating label directly is safe here.

Keep event handlers short. Anything that takes more than a few milliseconds (network calls, file I/O, heavy computation) must be moved to a background thread. Long handlers freeze the UI because they block the Application Thread from rendering the next frame.

Step 3 — Layout Containers Position Children

VBox stacks its children vertically with a configurable spacing. The first argument to its constructor is the gap in pixels between children. You can also set padding and alignment via inline CSS (the -fx-* property names) or via the Java API. Other common containers you will meet soon are HBox (horizontal), BorderPane (north/south/east/west/center), and GridPane (rows and columns).

You add children with getChildren().addAll(...). The getChildren() method returns an ObservableList<Node> — adding or removing items from it automatically triggers a layout pass and a re-render.

Step 4 — The Scene Owns a Root Node and a Size

new Scene(root, 320, 200) creates a scene whose root is your VBox and whose initial size is 320×200 pixels. Every control inside root is now part of this scene's node tree. The Scene object is also where you later attach stylesheets:

scene.getStylesheets().add( getClass().getResource("/styles/app.css").toExternalForm() );

Step 5 — The Stage Is the OS Window

primaryStage is provided by the toolkit when it calls start. You set a title, attach the scene, and call show(). That is all it takes to push a window onto the screen. The stage is also where you control resizability, full-screen mode, icons, and minimum/maximum dimensions:

primaryStage.setResizable(false); primaryStage.setMinWidth(200); primaryStage.getIcons().add( new Image(getClass().getResourceAsStream("/icon.png")) );

The Complete Object Graph

After show() the live object graph looks like this:

Stage (OS window) └── Scene (320 × 200) └── VBox [root node] ├── Label "Click the button below" └── Button "Say Hello"

This is the scene graph in action. When you click the button, the label's text property changes; the JavaFX rendering engine notices the change (because Text is observable) and repaints just the affected region on the next pulse — no manual repaint calls needed.

Closing Behaviour

By default, closing the primary stage exits the application via Platform.exit(). If you have multiple stages or need to run cleanup code, you can override stop() in your Application subclass:

@Override public void stop() throws Exception { // release resources, close database connections, etc. System.out.println("Application shutting down"); }
Never call System.exit() to close a JavaFX app. It bypasses stop() and skips any cleanup you registered. Use Platform.exit() instead, or simply close the window and let the default behaviour run.

Running the Application

With the JavaFX SDK on the module path (e.g. via the org.openjfx:javafx-controls Maven/Gradle dependency), compile and run as a normal Java class. IntelliJ IDEA and VS Code both support JavaFX projects through their Maven/Gradle integration — you do not need a separate plugin for this basic setup.

Summary

A working JavaFX window needs five things: controls created as objects, an event handler wiring user actions to code, a layout container assembling the controls, a Scene giving the tree a size, and a Stage presenting it on screen. The runtime calls start() for you — your job is to build the tree and call show(). That pattern scales unchanged from this 30-line program to a multi-screen enterprise application.