<node>

<node> is the plain container: one bevy_ui Node entity that takes a style, lays out its children with flexbox or grid, and reports pointer events. There are no divs or spans; every layout is built from <node>. Most of this page (the style prop, state styles, events, identity props) applies to every styled element, <button>, <text> and <image> included.

Usage

<node style={{ flexDirection: "row", gap: 12, padding: 16 }}>
  <node style={{ width: 48, height: 48, backgroundColor: "#7aa2f7" }} />
  <node style={{ width: 48, height: 48, backgroundColor: "#9ece6a" }} />
</node>

A <node> starts with Bevy's defaults: a flex container laid out as a row, sized by its content, with a transparent background, and passing the pointer through to whatever is behind it. Arrange children with flexbox, grid or positioning, and size and space them with sizing and spacing.

Top-level layout

Elements rendered at the top of your React tree are children of a window-filling flex column that centers them horizontally and spaces them 16px apart. To lay out the whole window yourself, render a single node that fills it:

export function App() {
  return <node style={{ width: "100%", height: "100%" }}>{/* … */}</node>;
}

Common props

Prop Effect Details
style Layout and paint, as a BevyStyle object Style properties
hoverStyle, pressStyle, focusStyle Style overlays for interaction states State styles below
onClick, onPointer* Click, drag and hover events Mouse
onWheel Raw wheel deltas over the element Mouse
scrollTop, scrollLeft, scrollStep Controlled scrolling of an overflow: "scroll" container Overflow
onScroll The scroll offset changed Overflow
name Names the Bevy entity, so Rust systems can find it Named nodes
sharedTag Shared-element identity across unmount and mount Shared elements
key React's list key React

Not every element takes every group: <editableText> has no pointer, scroll or wheel props, <root> and nested <text> spans take only style, name, sharedTag and key. A prop outside an element's groups is ignored with a propIgnored devtools warning. The element reference lists the groups per element.

Every prop crosses to Bevy only when its value changes, so re-rendering with the same props costs nothing on the Bevy side. Unsetting a style property returns it to the element's default (for example focusPolicy returns to "block" on a <button>).

State styles

hoverStyle, pressStyle and focusStyle overlay the base style while the element is in that state. They are applied on the Bevy side: no React state, no re-render.

<node
  style={{
    padding: 12,
    borderRadius: 8,
    backgroundColor: "#24283b",
    transition: { backgroundColor: { duration: 150 } },
  }}
  hoverStyle={{ backgroundColor: "#414868" }}
  pressStyle={{ backgroundColor: "#565f89" }}
/>
  • The overlays stack in order: style, then hoverStyle while hovered, then pressStyle while pressed (a pressed element is also hovered), then focusStyle while focused, so focusStyle wins any conflict.
  • A state style carries any style property and only changes what it sets. When the state ends, those properties return to the base style.
  • With a transition on the base style, state changes ease instead of snapping. See Style transitions.
  • Hover covers every element under the pointer, from the topmost down to the first one that blocks (focusPolicy: "block"). A hovered child keeps its pass-through ancestors hovered too, but a <button> on top un-hovers everything beneath it, its ancestors included.
  • pressStyle follows the primary mouse button or a touch. It stays applied from the press until the release, even if the pointer leaves the element meanwhile.
  • { animated } bindings work in the base style only. A binding in a state style is ignored with a styleBinding warning.
  • Only <editableText> takes focus on its own (by click or autofocus). On other elements, focusStyle applies only while your Rust code gives the entity Bevy's InputFocus.

Pointer behavior

  • A <node> passes the pointer through by default. Even with an onClick, a press also reaches what is behind it, including the 3D scene, so a camera drag can start under your panel. Use a <button> or focusPolicy: "block" for controls and panels that should catch the pointer. See Focus policy.
  • Clicks do not bubble. A click goes to the topmost element under the pointer that has an onClick or any onPointer* handler, or is a <button> or <editableText>, and to no other element. Clicking a <text> label inside a clickable node fires the node's onClick.
  • onPointer* handlers alone make an element own clicks: a child with only onPointerEnter swallows clicks meant for an onClick ancestor.

Limits

  • No CSS cascade: styles never inherit from a parent <node>, text styles included. Share style objects in JavaScript instead.
  • Children are laid out by Bevy's flexbox and grid only; there is no inline or block flow.

See <node> in the element reference, and Common props for every shared prop's type.