ResizeObserver

ResizeObserver is a Java wrapper around the browser's ResizeObserver API. It lets you observe size changes of any Vaadin component and react to them in server-side Java code.

Why use it?

Vaadin's built-in Page.addBrowserWindowResizeListener only tells you about the browser window size and you have to wait for the first resize event (or combine it with retrieveExtendedClientDetails) to get the initial dimensions.

ResizeObserver improves on this in several ways:

  • Reports the size of any component, not just the window.
  • Fires the initial size immediately – no need for a separate “get current size” call.
  • Events are debounced (100 ms by default) to avoid flooding the server during continuous resizing.
  • Proper cleanup on detach – listeners are removed automatically when the observed component is detached from the UI.

Getting the instance

There is one ResizeObserver per UI. Obtain it with:

// Using the current UI (most common)
ResizeObserver ro = ResizeObserver.get();

// Or for a specific UI
ResizeObserver ro = ResizeObserver.of(myUi);

Observing a single component

There are two listener styles. Pick whichever fits your code better.

Simple listener

The observe method accepts a component and a SizeChangeListener that receives a Dimensions record:

ResizeObserver.get().observe(myComponent, dimensions -> {
    int w = dimensions.width();
    int h = dimensions.height();
    // react to new size
});

You can chain multiple observe calls:

ResizeObserver.get()
    .observe(header, d -> header.setText("Header: " + d.width() + "px"))
    .observe(grid, d -> {
        if (d.width() > 600) {
            grid.setColumns("name", "email", "phone", "role");
        } else {
            grid.setColumns("name", "email");
        }
    });

Vaadin core-style listener

The addResizeListener method returns a Registration and delivers a SizeChangeEvent:

Registration reg = ResizeObserver.get()
    .addResizeListener(myComponent, event -> {
        int width = event.getWidth();
        int height = event.getHeight();
        Dimensions d = event.getDimensions();
    });

// Later, stop listening:
reg.remove();

Observing multiple components together

When you need coordinated dimensions of several components at once (e.g. to draw a line between two elements), use the multi-component overload:

Registration reg = ResizeObserver.get()
    .observe((dimensionsMap) -> {
        Dimensions d1 = dimensionsMap.get(button1);
        Dimensions d2 = dimensionsMap.get(button2);
        // both are fresh, measured at the same time
        drawLineBetween(d1, d2);
    }, button1, button2);

Whenever any of the observed components changes size, the listener receives a map with up-to-date Dimensions for all of them.

The Dimensions record

Dimensions mirrors the browser's DOMRectReadOnly plus offset properties:

Field Description
x, y Origin of the content rect
width, height Content box size
top, right, bottom, left Edge positions
offsetLeft, offsetTop Element offset position
offsetWidth, offsetHeight Element offset size (includes borders)

Stopping observation

With the simple API, pass the same listener reference to unobserve:

ResizeObserver.SizeChangeListener listener = d -> { /* ... */ };
ResizeObserver.get().observe(component, listener);

// Later:
ResizeObserver.get().unobserve(component, listener);

With the Vaadin-style API, use the returned Registration:

Registration reg = ResizeObserver.get().addResizeListener(component, e -> { /* ... */ });
reg.remove();

Tuning the debounce timeout

By default, resize events are debounced at 100 ms. You can change this:

ResizeObserver.get().withDebounceTimeout(250); // 250 ms

Full example: responsive Grid

Viritin's VGrid (and other components implementing FluentHasSize) provides a convenient addResizeListener helper that observes the component's own size. This makes it easy to build a Grid that adapts its columns to the available space – showing all columns on wide screens and collapsing to a single essential column on handheld-sized devices:

@Route
public class MyResponsiveGrid extends VGrid<Person> {
    private Mode mode;

    public MyResponsiveGrid() {
        super(Person.class);
        addResizeListener(event -> {
            configureColumns(event.getWidth() < 800 ? Mode.MOBILE : Mode.DESKTOP);
        });
    }

    private void configureColumns(Mode mode) {
        if (this.mode != mode) {
            this.mode = mode;
            if (mode == Mode.MOBILE) {
                setColumns("name");
            } else {
                setColumns("name", "email", "phone", "role");
            }
        }
    }

    enum Mode {
        MOBILE, DESKTOP
    }
}

The addResizeListener on FluentHasSize is a shorthand that delegates to ResizeObserver.get().addResizeListener(this, listener). The guard if (this.mode != mode) avoids reconfiguring columns on every resize event when the mode hasn't actually changed.

Because the listener reacts to the component's own width (not the browser window), the Grid adapts correctly even when placed inside a SplitLayout or a narrow sidebar.