
Security News
Axios Supply Chain Attack Reaches OpenAI macOS Signing Pipeline, Forces Certificate Rotation
OpenAI rotated macOS signing certificates after a malicious Axios package reached its CI pipeline in a broader software supply chain attack.
onejs-react
Advanced tools
React 19 reconciler for Unity's UI Toolkit.
| File | Purpose |
|---|---|
src/host-config.ts | React reconciler implementation (createInstance, commitUpdate, etc.) |
src/renderer.ts | Entry point: render(element, container) |
src/components.tsx | Component wrappers: View, Text, Label, Button, TextField, etc. |
src/screen.tsx | Responsive design: ScreenProvider, useBreakpoint, useScreenSize, useResponsive |
src/types.ts | TypeScript type definitions (includes Vector Drawing types) |
src/index.ts | Package exports |
| Component | UI Toolkit Element | Description |
|---|---|---|
View | VisualElement | Container element |
Text | TextElement | Primary text display |
Label | Label | Form labels, semantic labeling |
Button | Button | Interactive button |
TextField | TextField | Text input |
Toggle | Toggle | Checkbox/toggle |
Slider | Slider | Numeric slider |
ScrollView | ScrollView | Scrollable container |
Image | Image | Image display |
ListView | ListView | Virtualized list |
Raw text in JSX (e.g., <View>Hello</View>) creates a TextElement, providing semantic distinction from explicit <Label> components.
import { render, View, Text, Label, Button } from 'onejs-react';
function App() {
return (
<View style={{ padding: 20 }}>
<Text text="Welcome!" style={{ fontSize: 24 }} />
<Button text="Click me" onClick={() => console.log('clicked')} />
<View>Raw text also works</View>
</View>
);
}
render(<App />, __root);
OneJS has multiple type sources. Here's when to use each:
Import types from onejs-react for refs and component props:
import { View, Button, VisualElement, ButtonElement } from "onejs-react"
function MyComponent() {
const viewRef = useRef<VisualElement>(null)
const buttonRef = useRef<ButtonElement>(null)
useEffect(() => {
buttonRef.current?.Focus()
}, [])
return (
<View ref={viewRef}>
<Button ref={buttonRef} text="Click me" />
</View>
)
}
For creating elements outside React, use unity-types:
import { Button } from "UnityEngine.UIElements"
const btn = new Button()
btn.text = "Dynamic Button"
__root.Add(btn)
The render() function accepts any RenderContainer:
import { render, RenderContainer } from "onejs-react"
// __root is provided by the runtime
render(<App />, __root)
RenderContainer (minimal: __csHandle, __csType)
└── VisualElement (full API: style, hierarchy, events)
├── TextElement (+ text property)
│ ├── LabelElement
│ └── ButtonElement
├── TextFieldElement (+ value, isPasswordField, etc.)
├── ToggleElement (+ value: boolean)
├── SliderElement (+ value, lowValue, highValue)
└── ScrollViewElement (+ scrollOffset, ScrollTo)
ojs- prefix internally (e.g., ojs-view, ojs-button) to avoid conflicts with HTML typespadding/margin are expanded to individual properties (UI Toolkit requirement)__eventAPI from QuickJSBootstrap.js{ element, type, props, eventHandlers: Map, appliedStyleKeys: Set }npm run typecheck # TypeScript check (no build output - consumed directly by App)
npm test # Run test suite
npm run test:watch # Run tests in watch mode
Test suite uses Vitest with mocked Unity CS globals. Tests are in src/__tests__/:
| File | Coverage |
|---|---|
host-config.test.ts | Instance creation, style/className management, events, children |
renderer.test.tsx | Integration tests: render(), unmount(), React state, effects |
components.test.tsx | Component wrappers, prop passing, event mapping |
mocks.ts | Mock implementations of Unity UI Toolkit classes |
setup.ts | Global test setup for CS, __eventAPI |
OneJS exposes Unity's Painter2D API for GPU-accelerated vector graphics. Any element can render custom vector content via onGenerateVisualContent.
import { View, render } from "onejs-react"
function Circle() {
return (
<View
style={{ width: 200, height: 200, backgroundColor: "#333" }}
onGenerateVisualContent={(mgc) => {
const p = mgc.painter2D
// Draw a filled circle
p.fillColor = new CS.UnityEngine.Color(1, 0, 0, 1) // Red
p.BeginPath()
p.Arc(
new CS.UnityEngine.Vector2(100, 100), // center
80, // radius
CS.UnityEngine.UIElements.Angle.Degrees(0),
CS.UnityEngine.UIElements.Angle.Degrees(360),
CS.UnityEngine.UIElements.ArcDirection.Clockwise
)
p.Fill(CS.UnityEngine.UIElements.FillRule.NonZero)
}}
/>
)
}
Path operations:
BeginPath() - Start a new pathClosePath() - Close the current subpathMoveTo(point) - Move to point without drawingLineTo(point) - Draw line to pointArc(center, radius, startAngle, endAngle, direction) - Draw arcArcTo(p1, p2, radius) - Draw arc tangent to two linesBezierCurveTo(cp1, cp2, end) - Cubic bezier curveQuadraticCurveTo(cp, end) - Quadratic bezier curveRendering:
Fill(fillRule) - Fill the current pathStroke() - Stroke the current pathProperties:
fillColor - Fill color (Unity Color)strokeColor - Stroke color (Unity Color)lineWidth - Stroke width in pixelslineCap - Line cap style (Butt, Round, Square)lineJoin - Line join style (Miter, Round, Bevel)Use MarkDirtyRepaint() to trigger a repaint when drawing state changes:
function AnimatedCircle() {
const ref = useRef<VisualElement>(null)
const [radius, setRadius] = useState(50)
useEffect(() => {
// Trigger repaint when radius changes
ref.current?.MarkDirtyRepaint()
}, [radius])
return (
<View
ref={ref}
style={{ width: 200, height: 200 }}
onGenerateVisualContent={(mgc) => {
const p = mgc.painter2D
p.fillColor = new CS.UnityEngine.Color(0, 0.5, 1, 1)
p.BeginPath()
p.Arc(
new CS.UnityEngine.Vector2(100, 100),
radius,
CS.UnityEngine.UIElements.Angle.Degrees(0),
CS.UnityEngine.UIElements.Angle.Degrees(360),
CS.UnityEngine.UIElements.ArcDirection.Clockwise
)
p.Fill(CS.UnityEngine.UIElements.FillRule.NonZero)
}}
/>
)
}
| Feature | Unity Painter2D | HTML5 Canvas |
|---|---|---|
| Transforms | Manual point calculation | Built-in translate/rotate/scale |
| Gradients | Limited (strokeGradient) | Full linear/radial/conic |
| State Stack | Not built-in | save()/restore() |
| Text | Via MeshGenerationContext.DrawText() | fillText/strokeText |
| Shadows | Not available | shadowBlur, shadowColor |
| Clipping | Via nested VisualElements | clip() path-based |
The following types are re-exported from unity-types:
type Vector2 = CS.UnityEngine.Vector2
type Color = CS.UnityEngine.Color
type Angle = CS.UnityEngine.UIElements.Angle
type ArcDirection = CS.UnityEngine.UIElements.ArcDirection
type Painter2D = CS.UnityEngine.UIElements.Painter2D
type MeshGenerationContext = CS.UnityEngine.UIElements.MeshGenerationContext
type GenerateVisualContentCallback = (context: MeshGenerationContext) => void
toArray<T>(collection): T[]Converts C# collections (List<T>, arrays) to JavaScript arrays. C# collections exposed through the OneJS proxy have .Count/.Length and indexers but lack .map(), .filter(), and other array methods.
import { toArray } from "onejs-react"
// Convert a C# List for use in JSX
{toArray<Item>(inventory.Items).map(item => <ItemView key={item.Id} item={item} />)}
// Convert a C# array
const resolutions = toArray<Resolution>(Screen.resolutions)
// Safe with null — returns []
const npcs = toArray(currentPlace?.NPCs)
Supports objects with .Count (List, IList) or .Length (C# arrays). Returns [] for null/undefined.
react-reconciler@0.31.x (React 19 compatible)vitest (dev) - Test runnerreact@18.x || 19.xFAQs
React 19 renderer for OneJS (Unity UI Toolkit)
We found that onejs-react demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
OpenAI rotated macOS signing certificates after a malicious Axios package reached its CI pipeline in a broader software supply chain attack.

Security News
Open source is under attack because of how much value it creates. It has been the foundation of every major software innovation for the last three decades. This is not the time to walk away from it.

Security News
Socket CEO Feross Aboukhadijeh breaks down how North Korea hijacked Axios and what it means for the future of software supply chain security.