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

# Wake Lock (since V25.2)

The `WakeLock` API gives server-side Java code access to the [browser Screen Wake Lock API](https://developer.mozilla.org/en-US/docs/Web/API/Screen_Wake_Lock_API). It keeps the screen from dimming or locking while the lock is held — useful for dashboards left on display, kiosks, presentations, navigation, media playback, and similar always-on views. It exposes two static entry points — `WakeLock.request()` to acquire and `WakeLock.release()` to release — and a pair of reactive signals for observing state and availability.

> **Note:** The Screen Wake Lock API requires a secure context (HTTPS), except on `localhost` during development. Safari supports it from version 16.4. The browser may also release the lock at any time — for example, when the tab becomes hidden, on low battery, or under operating-system power-saving rules — and the application has no control over that. Use `WakeLock.availabilitySignal()` to gate UI controls up front and `WakeLock.activeSignal()` to react to spontaneous releases.

## <a id="acquiring-a-wake-lock"></a>Acquiring a Wake Lock

Use `WakeLock.request()` to ask the browser to keep the screen on. The call is asynchronous and fire-and-forget: by the time the method returns the browser has not necessarily granted the lock yet. Observe `WakeLock.activeSignal()` for the actual state — see [Observing Active State](#observing-active-state).

```java
Button keepOn = new Button("Keep screen on", e -> WakeLock.request());
```

The framework adds two conveniences on top of the raw browser API:

- The browser releases the lock automatically when the tab becomes hidden. The client transparently re-acquires it when the tab becomes visible again, so a single `request()` call covers the lifetime of the view; no `visibilitychange` listener is needed.

- Calling `request()` while a lock is already held is a no-op.

For persistent failures — the API is unavailable, the origin is insecure, or the browser refuses with `NotAllowedError` — there is a `request(onError)` overload covered in [Handling Errors](#handling-errors).

## <a id="releasing-a-wake-lock"></a>Releasing a Wake Lock

Call `WakeLock.release()` to drop the current lock and stop re-acquiring it on subsequent visibility changes. The call is idempotent — calling it when no lock is held is a no-op.

```java
Button stop = new Button("Allow screen off", e -> WakeLock.release());
```

## <a id="observing-active-state"></a>Observing Active State

`WakeLock.activeSignal()` returns a read-only `Signal<Boolean>` that reflects whether the browser is currently holding the wake lock for this UI. It starts as `false`, flips to `true` once the browser confirms a request, and flips back to `false` whenever the browser releases the lock — explicitly through `release()`, automatically when the tab becomes hidden, or because the browser dropped it for power-saving or low-battery reasons. When the tab is shown again the client re-requests the lock if `release()` has not been called, so the signal flips back to `true` shortly after.

Use `Signal.effect()` to react to changes — for example, to reflect the wake-lock state in the browser’s document title so a hidden tab makes its stay-on mode obvious:

```java
Signal.effect(this, () -> {
    Page page = UI.getCurrentOrThrow().getPage();
    page.setTitle(WakeLock.activeSignal().get()
            ? "Live Dashboard (stay-on)"
            : "Live Dashboard");
});
```

When a component property accepts a signal directly, prefer that binding over `Signal.effect()`. The canonical "Start" / "Stop" toggle pair binds the signal to button visibility:

```java
Button keepOn = new Button("Keep screen on", e -> WakeLock.request());
keepOn.bindVisible(Signal.not(WakeLock.activeSignal()));

Button allowOff = new Button("Allow screen off", e -> WakeLock.release());
allowOff.bindVisible(WakeLock.activeSignal());
```

## <a id="handling-errors"></a>Handling Errors

Pass a `SerializableConsumer<WakeLockError>` to `WakeLock.request()` to be notified when the browser permanently refuses the request. The callback fires on the UI thread. Switch on `err.code()` to get a typed `WakeLockErrorCode` — the `switch` is exhaustive at compile time:

```java
WakeLock.request(err -> {
    String userMessage = switch (err.code()) {
        case UNSUPPORTED ->
            "Keep-screen-on isn't available in this browser.";
        case NOT_ALLOWED ->
            "The browser refused to keep the screen on.";
        case UNKNOWN ->
            "Couldn't keep the screen on.";
    };
    Notification.show(userMessage);
});
```

The callback is meant for **persistent** failures — the kinds that should surface in the UI, typically by re-enabling a "Keep screen on" toggle and showing a message. It is not invoked when the lock is merely deferred (tab hidden until the user returns) or when the browser temporarily releases the lock for power-management reasons; observe `activeSignal()` for those.

When availability is already known to be `UNSUPPORTED` (see [Checking Availability](#checking-availability)), the error callback fires synchronously, without a server-to-client round-trip. This makes it cheap to wire the toggle up unconditionally and rely on the error callback to degrade gracefully on insecure origins.

For diagnostics, log `err.message()` alongside the code — it is a free-form browser string and isn’t meant for end users.

## <a id="checking-availability"></a>Checking Availability

`WakeLock.availabilitySignal()` returns a `Signal<WakeLockAvailability>` reporting whether the Screen Wake Lock API is usable in the current page context. Reading the signal does not call the browser API — it reports whether a subsequent `request()` has any chance of succeeding.

- `SUPPORTED`

  The browser exposes the API and the page is served from a secure context. Render the "keep screen on" toggle.

- `UNSUPPORTED`

  The browser does not implement the API, or the page is served over an insecure connection. No user action changes this — hide the toggle entirely.

- `UNKNOWN`

  The browser has not yet reported a value. This is the seed state during the brief window between UI attach and bootstrap completion; in practice applications see it only as a transient state.

Use `bindVisible()` to render the toggle only when the API is usable:

```java
Button keepOn = new Button("Keep screen on", e -> WakeLock.request());
keepOn.bindVisible(WakeLock.availabilitySignal()
        .map(a -> a == WakeLockAvailability.SUPPORTED));
```

## <a id="calling-from-background-threads"></a>Calling from Background Threads

The static entry points above resolve the target UI through `UI.getCurrent()`, which only works on a request-handling thread. Background threads (executors, scheduled tasks, push handlers) need to pass the UI explicitly. Every entry point has an overload accepting a `UI` argument: `request(ui)`, `request(onError, ui)`, `release(ui)`, `activeSignal(ui)`, and `availabilitySignal(ui)`.

```java
@Override
protected void onAttach(AttachEvent event) {
    UI ui = event.getUI();
    ScheduledFuture<?> task = scheduler.schedule(
            () -> WakeLock.request(ui),
            5, TimeUnit.SECONDS);
    addDetachListener(e -> task.cancel(false));
}
```

The error callback on `request(onError, ui)` is still invoked on the UI thread, so it can update components directly without an `ui.access()` wrapper.

## <a id="limitations-and-caveats"></a>Limitations and Caveats

- A secure context is required (HTTPS, or `localhost` during development).

- Safari supports the API from 16.4; older versions report `UNSUPPORTED` through `availabilitySignal()`.

- The browser may release the lock at any time — low battery, power-saving mode, or operating-system policy. `activeSignal()` reflects the change so the UI can react.

- Wake lock state is per-UI (per tab). Each tab needs its own `request()`; releasing in one tab does not affect another.
