Docs
Components

React

Register the root component

import ResourcesWebGPU from '@uno.build/ui/ResourcesWebGPU'
import UI from '@uno.build/ui/UI'
import { registerRootComponent } from '@uno.build/ui/react'
import App from './App.jsx'

const canvas = document.querySelector('canvas')
const resources = await ResourcesWebGPU.create({ canvas })
const { ui } = await UI.create({ resources })

// React
const react = registerRootComponent(App, { ui })
react.mount({})

React commits call ui.update() automatically. Keep the viewport setup and render loop from First Layout to draw the UI with ui.draw().

Use one registered root per UI. Call react.mount(props) again to update the root component's props while preserving its state.

Call react.unmount() to remove the component tree and run its cleanup before destroying the UI. It does not destroy the UI or dispose shared resources; see Destroy.

Components

@uno.build/ui/react exports five components: View, Text, Image, ScrollView, and Input. They render uno nodes and share the properties and event props below. Each component's section describes its additional props and behavior.

<View>

View is a container for layout, backgrounds, and child components. Rendering a View creates an uno node, equivalent to calling ui.create() in the imperative API. React adds the node to its parent and manages its updates and removal. Use React state to update its styles in response to events.

// ReactView.jsx
import { useState } from 'react'
import { View } from '@uno.build/ui/react'

export default function App({}) {
  const [hovered, setHovered] = useState(false)

  return (
    <View
      style={{
        width: '100%',
        height: '100%',
        alignItems: 'center',
        justifyContent: 'center',
      }}
    >
      <View
        onPointerOver={() => setHovered(true)}
        onPointerOut={() => setHovered(false)}
        style={{
          width: '200px',
          height: '150px',
          backgroundColor: '#8080FD',
          opacity: hovered ? '0.5' : '1',
        }}
      ></View>
    </View>
  )
}

<Text>

Use Text for all text content. Follow Texts & Fonts to load and register a font before rendering the examples below.

import { useState } from 'react'
import { View, Text } from '@uno.build/ui/react'

export default function App() {
  return (
    <View style={{ padding: '16px', gap: '8px' }}>
      <Text style={{ color: '#ffffff' }}>Click the counter to update it.</Text>
    </View>
  )
}

A string directly inside View, such as <View>Hello</View>, is invalid. Text is a leaf node: it cannot contain elements, components, or React fragments. To use different styles within a line, place sibling Text components inside a View with flexDirection: 'row'.

<Image>

Image displays a registered image resource. It does not download the image from src. Follow Images to load and register the asset before calling react.mount({}).

PropertyValueDefaultUsage
srcStringRequiredThe registered image resource identifier.
widthStringNot setWidth with units, such as '128px'. If only this dimension is set, the registered aspect ratio is preserved.
heightStringNot setHeight with units, such as '100px'. If only this dimension is set, the registered aspect ratio is preserved.
style.objectFit'fill', 'contain', 'cover', or 'none''fill'Controls how the image fits within the component's dimensions. Set it inside style.

With neither dimension set, the component uses the registered image size. Dimensions in style take precedence over the width and height props.

Use the same identifier as the component's src:

import { Image, View } from '@uno.build/ui/react'

export default function App() {
  return (
    <View style={{ flexDirection: 'row', gap: '16px' }}>
      <Image src="assets/coin.png" width="128px" />
      <Image src="assets/coin.png" width="160px" height="100px" style={{ objectFit: 'contain' }} />
    </View>
  )
}

<ScrollView>

ScrollView clips its children and provides vertical scrolling by default.

PropertyValueDefaultUsage
horizontalBooleanfalseArranges the content in a row and enables horizontal scrolling when true.

Give it a limited height so that content can overflow the viewport:

import { ScrollView, Text, View } from '@uno.build/ui/react'

const ITEMS = Array.from({ length: 20 }, (_, index) => index + 1)

export default function App() {
  return (
    <ScrollView style={{ width: '240px', height: '200px' }}>
      {ITEMS.map((item) => (
        <View key={item} style={{ padding: '12px', flexShrink: '0', backgroundColor: '#8080FD' }}>
          <Text style={{ color: '#ffffff' }}>Item {item}</Text>
        </View>
      ))}
    </ScrollView>
  )
}

Set horizontal to arrange the content in a row and enable horizontal scrolling:

<ScrollView horizontal style={{ width: '240px', height: '80px' }}>
  {ITEMS.map((item) => (
    <View key={item} style={{ width: '120px', padding: '12px', flexShrink: '0' }}>
      <Text style={{ color: '#ffffff' }}>Item {item}</Text>
    </View>
  ))}
</ScrollView>

The style prop applies to the viewport. Use onScroll event to respond to scrolling.

<Input>

Input is a visual component controlled by value. It displays text, a placeholder, and a blinking caret while focused.

PropertyValueDefaultUsage
valueString, number, or nullundefinedThe displayed input value. An empty string, null, or undefined is treated as empty.
placeholderString, number, or nullundefinedText displayed only when value is empty and the input is unfocused.
placeholderTextColorColor string'#777777'The color used for the placeholder text.

Pass the displayed text through value:

import { Input } from '@uno.build/ui/react'

export default function App({ value = '' }) {
  return (
    <Input
      value={value}
      placeholder="Your name"
      style={{ width: '240px', height: '40px', padding: '8px', color: '#111827' }}
    />
  )
}

Input does not create a DOM input or handle IME, selection, or clipboard input. To support typing in a browser, connect a native <input> outside the uno component tree, synchronize its value with React state, and manage its focus through onFocus and onBlur. See the demo below for a complete example.

Event props

All five components accept the following event props. Pass a handler function to subscribe; omit the prop or set it to null or undefined to remove your handler. React manages the listener registration and cleanup.

Propuno eventWhen it fires
onPointerDownpointerdownA pointer is pressed on a node.
onPointerMovepointermoveA pointer moves over the UI or during an active pointer interaction.
onPointerUppointerupAn active pointer is released.
onPointerCancelpointercancelThe platform cancels an active pointer interaction.
onPointerOverpointeroverA node becomes the pointer's hover target.
onPointerOutpointeroutA node stops being the pointer's hover target.
onClickclickA node is activated by a completed click or tap.
onWheelwheelWheel or trackpad input occurs over a node.
onScrollscrollA scroll container's offset changes through scrolling.
onFocusfocusA node gains uno focus.
onBlurblurA node loses uno focus.

Handlers receive uno events rather than React DOM synthetic events. Each event includes these common members:

MemberMeaning
typeThe uno event name, such as 'pointerdown'.
targetThe uno node where the event originated.
current_targetThe uno node whose handler is currently running.
source_eventThe original platform event, or null for a programmatic event without a source.
stopPropagation()Stops the event from continuing to ancestor nodes.

Events bubble through the uno node tree, so a component's handler can receive events from its descendants. Event-specific data, such as pointer coordinates, wheel deltas, scroll offsets, and focus transitions, is documented in Events.

Refs and handles

Pass a React ref to access a component's uno nodes. These refs expose handles rather than DOM elements. Import NodeHandle, ScrollViewHandle, and InputHandle from @uno.build/ui/react when using TypeScript.

ComponentRef valueAvailable members
View, Text, ImageNodeHandlenodes.main
ScrollViewScrollViewHandlenodes.main (viewport), nodes.content (scrollable content)
InputInputHandlenodes.main, nodes.content, nodes.text, nodes.caret, focus(), blur()

nodes.caret is null when the input is unfocused. focus() and blur() change uno focus; they do not open a platform keyboard or provide text input.

useUI()

useUI() is a React hook that returns the UI passed to registerRootComponent. Call it only inside that registered component tree. For example, a component can read the original dimensions of the registered assets/coin.png image:

import { Text, useUI } from '@uno.build/ui/react'

export function ImageSize() {
  const ui = useUI()
  const { width, height } = ui.resources.getImageSize('assets/coin.png')

  return (
    <Text style={{ color: '#ffffff' }}>
      Image size: {width} × {height}
    </Text>
  )
}

Demo

On this page