> Markdown version of [Fullscreen](https://vaadin.com/docs/next/flow/browser-apis/fullscreen-api). Section index: [llms.txt](https://vaadin.com/docs/next/flow/llms.txt)

# Fullscreen (since V25.2)

The `Fullscreen` API gives server-side Java code access to the [browser Fullscreen API](https://developer.mozilla.org/en-US/docs/Web/API/Fullscreen_API). It can fullscreen the entire page or a single component, exit fullscreen on demand, and expose the current fullscreen state as a reactive signal.

> **Note:** The browser only allows a fullscreen request during a user gesture — a click, a key press, or a touch tap. The API enforces this by attaching requests to a trigger component, so the request runs inside the DOM event that fires it. Bypassing the API and calling `requestFullscreen` directly via `executeJs` from a constructor, a background thread, or a server push is rejected by the browser.

## <a id="entering-fullscreen"></a>Entering Fullscreen

Bind a fullscreen request to a component the user can click. `Fullscreen.onClick(Component)` returns a `FullscreenBinding` whose `enter()` method registers the request with that component’s click. The request then runs inside the browser’s click handler and inherits the user gesture:

```java
Button fullscreenButton = new Button("Fullscreen");
Fullscreen.onClick(fullscreenButton).enter();
```

> **Important:** `onClick(…​)` *arms* the request; it does not register a click listener you fill in later. The request must run inside the browser’s gesture window, so it has to be armed up front — calling `enter()` from an `addClickListener` callback runs one round-trip too late and the browser rejects it.
>
> Forgetting `enter()` altogether — a binding created with no action — fails loudly instead: the framework throws an `IllegalStateException` when the view renders, naming the unarmed binding.
>
> `exit()` is different: it needs no gesture and so *is* called from a normal handler. See [How the Click Binding Works](https://vaadin.com/docs/next/flow/browser-apis/clipboard-api.md#how-the-binding-works) in the Clipboard article for the full explanation of this arm-don’t-handle model.

To fullscreen a single component instead of the whole page, pass it to `enter(Component)`:

```java
Button fullscreenButton = new Button("Fullscreen video");
Fullscreen.onClick(fullscreenButton).enter(videoPanel);
```

Component fullscreen moves the target into a Vaadin wrapper element and hides the rest of the view, so themes and overlay components (`Notification`, `ComboBox` popups, dialogs) keep working while fullscreen is active. When fullscreen exits — whether programmatically, via the user pressing `Esc`, or because a later request supersedes this one — the component is restored to its original DOM position.

`enter(Component)` is safe to call before the target component is attached: the request is wired to the trigger as soon as the component attaches.

## <a id="exiting-fullscreen"></a>Exiting Fullscreen

Call `Fullscreen.exit()` to leave fullscreen mode. Unlike entering, exiting doesn’t require a user gesture, so it can be called from any handler, including a server push:

```java
Button closeButton = new Button("Close", e -> Fullscreen.exit());
```

The call is a no-op when the page isn’t currently fullscreen. The user can also press `Esc` — the browser handles that itself, and the state signal updates accordingly.

## <a id="toggling-fullscreen"></a>Toggling Fullscreen

"Click to enter, click to exit" is a natural request, but a single button can’t both *arm* an enter request for the gesture window and decide at click time to exit instead — the two needs pull in opposite directions. The canonical pattern keeps them separate: always arm `enter()`, let the user leave with `Esc` or a control wired to `Fullscreen.exit()`, and drive the visible affordance from `stateSignal()` so the right control shows in each state:

```java
Button enter = new Button("Fullscreen");
Fullscreen.onClick(enter).enter(videoPanel);

Button exit = new Button("Exit fullscreen", e -> Fullscreen.exit());

Signal<Boolean> isFullscreen = Fullscreen.stateSignal()
        .map(state -> state == FullscreenState.FULLSCREEN);
enter.bindVisible(isFullscreen.map(on -> !on));
exit.bindVisible(isFullscreen);
```

Because `exit()` needs no gesture, the exit control is an ordinary click handler — only the enter side has to be armed. Pressing `Esc` reaches the same state through `stateSignal()`, so the buttons stay in sync however the user leaves fullscreen.

## <a id="observing-fullscreen-state"></a>Observing Fullscreen State

`Fullscreen.stateSignal()` returns a read-only signal of the current fullscreen state. Use it to drive UI that depends on whether the page is fullscreen, or to hide controls in browsers that don’t support fullscreen at all:

```java
viewLayout.bindClassName("is-fullscreen", Fullscreen.stateSignal()
        .map(state -> state == FullscreenState.FULLSCREEN));

Signal.effect(this, () -> {
    if (Fullscreen.stateSignal().get() == FullscreenState.UNSUPPORTED) {
        fullscreenButton.setVisible(false);
    }
});
```

The signal value is one of four `FullscreenState` enum constants:

- `FULLSCREEN`

  The page is currently in fullscreen mode.

- `NOT_FULLSCREEN`

  Fullscreen is supported but the page is not in it. The starting state in most browsers.

- `UNSUPPORTED`

  The browser does not support fullscreen, or the document is not permitted to enter it (for example, inside an iframe without the right permissions policy). Fullscreen requests fail in this state.

- `UNKNOWN`

  Sentinel value used only before the first client handshake delivers a real state. The signal is seeded from the initial bootstrap, so user code essentially never observes `UNKNOWN` in practice.

The signal is per-UI: each browser tab has its own state.

## <a id="handling-the-outcome"></a>Handling the Outcome

The plain `enter()` and `enter(Component)` calls are fire-and-forget — the server never sees whether the browser accepted the request. To react to the outcome, use the overloads that take an `onSuccess` runnable and an `onError` consumer:

```java
Fullscreen.onClick(fullscreenButton).enter(videoPanel,
        () -> Notification.show("Entered fullscreen"),
        err -> Notification.show(
                "Could not enter fullscreen: " + err.message()));
```

Both callbacks fire on the UI thread, so they can update components directly. Pass `() → {}` or `err → {}` to opt out of one side.

The `onError` consumer receives a record with the browser’s error name and message. The spec-documented rejection is `NotAllowedError`, raised when the request isn’t backed by a user gesture or is blocked by the document’s permissions policy. The state signal already reflects `UNSUPPORTED` when the browser refuses outright, so the error callback is mainly useful for diagnostics and for surfacing a message to the user when a request fails despite a click backing it.

`F0E1A4B2-3D7A-4E61-9B2C-7C0D5A5C9F33`
