Window

The UI viewport's size reaches React through two built-ins: the resize event and the bevy.window.size() request, the bevy-react counterparts of the browser's resize event and window.innerWidth/innerHeight. Both are typed in every generated bevy.ts, with no Rust code or registration.

Usage

Seed the value with the request on mount, then follow the event:

import { useEffect, useState } from "react";
import { bevy, type WindowSize } from "./bevy"; // generated

function useWindowSize(): WindowSize | null {
  const [size, setSize] = useState<WindowSize | null>(null);
  useEffect(() => {
    bevy.window.size().then(setSize, () => {});
    return bevy.on("resize", setSize);
  }, []);
  return size;
}

What is measured

Both report a WindowSize, { width, height }, in logical pixels: the same unit as plain numbers in styles.

  • The size is the viewport of the default UI camera, the camera bevy_ui lays the UI out against (the one marked IsDefaultUiCamera). It stays correct when that camera renders to a viewport or an offscreen texture.
  • Without a camera marked IsDefaultUiCamera, the size of the app's window is used. With several windows and no such camera, there is no size.
  • Logical pixels depend on the display's scale factor, so moving the window to a display with another scale factor changes the size too.

The resize event

  • The first resize is sent once React's first render has been applied to the world, so listeners subscribed in the effects of your initial tree receive the starting size.
  • After that, one resize is sent per frame in which the size changed, and never for an unchanged size. A window being dragged larger sends one per frame.
  • A component that mounts later has missed the earlier events: call the request on mount, as in the usage example. The answer and the events travel in one ordered channel, so the newest value always lands last.

The size request

bevy.window.size() resolves with the current size at any time. It rejects with no UI viewport available when there is no viewport to measure, such as before a window exists or in a headless app, so always pass a rejection handler.

Responsive layout

Sizes that only depend on the viewport need no events: the vw, vh, vmin and vmax units re-resolve on every resize without a React render (see Units). Read the size when the tree itself has to change, such as a compact layout under a breakpoint:

function Shell() {
  const size = useWindowSize();
  if (!size) return null; // nothing until the size is known
  return size.width < 720 ? <CompactShell /> : <WideShell />;
}

Rendering nothing until the first size arrives avoids a first frame in the wrong layout. Each resize re-renders every subscribed component, so keep the subscription near the top and pass the derived value down, or share it through a React context.

Limits

  • Only the size is reported: no window position, focus, or scale factor.
  • In a multi-window app, mark the UI camera with IsDefaultUiCamera, or no size is reported.
  • A <canvas>'s own onResize reports that element's size, not the viewport's.

The built-in types are listed in the generated bevy.ts reference.