JavaFX Controls, Layouts & FXML

Layout Panes: HBox & VBox

18 min Lesson 3 of 12

Layout Panes: HBox & VBox

Every JavaFX application beyond a trivial single-button window needs to arrange multiple nodes on screen in a predictable way. JavaFX solves this through layout panes — containers that measure and position their children automatically according to a fixed algorithm. The two simplest and most frequently used panes are HBox, which lines nodes up horizontally, and VBox, which stacks them vertically. Mastering these two will get you through the majority of real-world form, toolbar, and panel layouts.

Why Not Just Use Absolute Positions?

JavaFX does expose an AnchorPane and a raw Pane where you can set layoutX/layoutY manually. Avoid that approach for general UI work. Hard-coded coordinates break the moment a user resizes the window, changes the system font size, or switches locale (Arabic text is wider). Layout panes recalculate positions on every resize automatically — that is the whole point.

HBox: Horizontal Row Layout

HBox places its children one after another from left to right. Each child gets its preferred width; the HBox itself grows to fit. You can control the gap between children and how children are aligned within the row.

import javafx.application.Application; import javafx.geometry.Insets; import javafx.geometry.Pos; import javafx.scene.Scene; import javafx.scene.control.Button; import javafx.scene.control.Label; import javafx.scene.control.TextField; import javafx.scene.layout.HBox; import javafx.stage.Stage; public class HBoxDemo extends Application { @Override public void start(Stage stage) { Label nameLabel = new Label("Name:"); TextField nameField = new TextField(); Button saveBtn = new Button("Save"); // spacing = gap in pixels between each child HBox row = new HBox(10, nameLabel, nameField, saveBtn); // Align all children to the vertical centre of the row row.setAlignment(Pos.CENTER_LEFT); // Add breathing room around the HBox itself row.setPadding(new Insets(12)); stage.setScene(new Scene(row, 400, 60)); stage.setTitle("HBox Demo"); stage.show(); } public static void main(String[] args) { launch(args); } }

Three things to notice:

  • The varargs constructor new HBox(spacing, child1, child2, ...) is the most concise way to build a row — no need for a separate getChildren().addAll(...) call.
  • setAlignment(Pos.CENTER_LEFT) controls how the row of children is positioned inside the HBox when it has surplus height.
  • setPadding(new Insets(top, right, bottom, left)) adds inner whitespace. new Insets(12) applies the same value on all four sides.

Making a Child Grow to Fill Remaining Space

The TextField in the example above uses only its preferred width. In a real search bar or form row you want it to expand and fill whatever space is left after the label and button have taken their share. Use the static helper HBox.setHgrow(node, Priority.ALWAYS):

HBox.setHgrow(nameField, Priority.ALWAYS);

This tells the layout engine: "give any extra horizontal space to nameField." If multiple children all request Priority.ALWAYS, they share the extra space equally.

Priority enum values: ALWAYS — grab extra space whenever available. SOMETIMES — use extra space only if no ALWAYS child is present. NEVER (the default) — stay at preferred size.

VBox: Vertical Stack Layout

VBox is the vertical mirror of HBox. Children are stacked top-to-bottom. The API is identical — spacing, alignment, padding, and grow priorities — except the axis is vertical and the grow constraint is VBox.setVgrow(node, Priority).

import javafx.geometry.Insets; import javafx.geometry.Pos; import javafx.scene.control.*; import javafx.scene.layout.*; // A simple login panel built with VBox VBox loginPanel = new VBox(14); // 14 px gap between children loginPanel.setAlignment(Pos.CENTER); loginPanel.setPadding(new Insets(24, 40, 24, 40)); Label title = new Label("Sign In"); title.setStyle("-fx-font-size: 22px; -fx-font-weight: bold;"); TextField emailField = new TextField(); emailField.setPromptText("Email"); PasswordField passwordField = new PasswordField(); passwordField.setPromptText("Password"); Button loginBtn = new Button("Log In"); loginBtn.setMaxWidth(Double.MAX_VALUE); // stretch button to full width loginPanel.getChildren().addAll(title, emailField, passwordField, loginBtn);

Setting loginBtn.setMaxWidth(Double.MAX_VALUE) is a common idiom: it removes the button's maximum-width constraint so the VBox can stretch it to match its own width, giving a "full-width button" appearance typical of login screens.

Nesting HBox Inside VBox (and Vice Versa)

Real layouts are rarely a single row or column. The pattern you will use constantly is composing panes: a VBox as the outer container whose children include one or more HBox rows.

// A form with labelled fields and an action row at the bottom VBox form = new VBox(10); form.setPadding(new Insets(16)); // Row 1: label + field for "First Name" HBox row1 = buildFormRow("First Name:", new TextField()); // Row 2: label + field for "Last Name" HBox row2 = buildFormRow("Last Name:", new TextField()); // Row 3: buttons right-aligned Button cancel = new Button("Cancel"); Button submit = new Button("Submit"); HBox actions = new HBox(8, cancel, submit); actions.setAlignment(Pos.CENTER_RIGHT); form.getChildren().addAll(row1, row2, actions); // -- helper -- private HBox buildFormRow(String labelText, TextField field) { Label label = new Label(labelText); label.setMinWidth(100); // fixed width keeps columns aligned HBox.setHgrow(field, Priority.ALWAYS); return new HBox(8, label, field); }
Give labels a fixed minWidth. When multiple form rows use the same label width all the input fields line up in a neat column, exactly as in a professional form. Without it, the alignment shifts per-row depending on each label's text length.

Spacing vs Padding vs Margin

Three distinct spacing mechanisms are available on HBox/VBox and it is worth knowing exactly what each one does:

  • Spacing — the gap between children, set on the pane (setSpacing() or the constructor argument).
  • Padding — the space between the pane's border and its children, set on the pane (setPadding(new Insets(...))).
  • Margin — extra space around a specific child, set per-node (HBox.setMargin(node, new Insets(...)) or VBox.setMargin(node, new Insets(...))).

In most forms, spacing and padding are sufficient. Use per-node margins when a single child needs to be pushed away from its siblings without affecting the general spacing of the row.

CSS Styling of HBox and VBox

Both panes accept CSS via their setStyle() method or via a stylesheet. The most useful properties are -fx-background-color, -fx-background-radius, -fx-border-color, and -fx-padding (which mirrors setPadding()). Prefer external stylesheets over inline setStyle() calls to keep UI logic and styling separate.

HBox toolbar = new HBox(8); toolbar.getStyleClass().add("toolbar"); // In your stylesheet (e.g. styles.css loaded on the Scene): // .toolbar { // -fx-background-color: #2c2c2c; // -fx-padding: 8 12 8 12; // }
Do not mix layout concerns and style concerns in setStyle(). Setting -fx-padding both in a stylesheet and via setPadding() in code causes one to silently override the other. Pick one approach and stay consistent across the application.

Summary

HBox and VBox are the bread-and-butter layout panes in JavaFX. Use HBox to build toolbars, button rows, and labelled form fields side by side. Use VBox to stack sections, panels, and form rows top to bottom. Compose them by nesting one inside the other, control white space with spacing/padding/margin, and drive growth behaviour with HBox.setHgrow / VBox.setVgrow. In the next lesson you will meet BorderPane and GridPane — panes suited to multi-region window layouts and precise tabular grids.