-
Notifications
You must be signed in to change notification settings - Fork 0
API Reference
@nrbx/react is a React 19-style API for Roblox. It keeps the familiar React component model, adds web-like HTML tags for Roblox GUI instances, resolves Tailwind-style className strings, and renders through @nrbx/react-roblox.
This page is the package reference for the public API surface. It is organized by module and concept so it is easy to browse from the GitHub Wiki.
import React, { useState } from "@nrbx/react";
import { createRoot } from "@nrbx/react-roblox";
const root = createRoot(new Instance("ScreenGui"));
function Counter() {
const [count, setCount] = useState(0);
return (
<React.Fragment>
<div className="flex items-center gap-2 p-4">
<button
className="rounded bg-blue-500 px-3 py-2 text-white"
onClick={() => setCount(c => c + 1)}
>
Count: {count}
</button>
</div>
</React.Fragment>
);
}
root.render(<Counter />);Creates a React element. In @nrbx/react, this wrapper does the following:
- resolves HTML tags to Roblox GUI classes (
div->Frame,button->TextButton) - auto-wraps text children (
"Hello",123) into Roblox text elements when needed - translates React-style events like
onClickinto RobloxEventtables - parses Tailwind-style
classNamevalues into Roblox properties
const el = React.createElement(
"button",
{ className: "bg-blue-500 p-2", onClick: () => print("clicked") },
"Save"
);Fragment component used for grouping multiple children without creating a wrapper instance.
const ui = (
<>
<h1>Title</h1>
<p>Body</p>
</>
);No-op wrapper. Roblox does not support the browser-style concurrent-mode dev checks, so this is kept as a compatibility layer.
<React.StrictMode>
<App />
</React.StrictMode>type ReactNode =
| ReactElement<any>
| string
| number
| boolean
| null
| undefined
| ReactNode[];
type ReactElement<P = any> = {
type: string | ComponentType<P>;
props: P & { children?: ReactNode };
key: string | null;
ref?: unknown;
};
type ComponentType<P = {}> =
| ((props: P) => ReactNode)
| (new (props: P) => Component<P, any>);
type FC<P = {}> = (props: P) => ReactNode;
type PropsWithChildren<P> = P & { children?: ReactNode };
type PropsWithRef<P, R = unknown> = P & { ref?: { current: R | null } };
type CSSProperties = Partial<Record<string, unknown>>;
type ClassName =
| string
| string[]
| Record<string, boolean>
| ReturnType<typeof cn>;Allowed child shapes for React elements.
The runtime element object created by React.createElement.
A class or function component constructor or callable component.
Function component type aliases.
const Card: React.FC<{ title: string }> = ({ title }) => <div>{title}</div>;Props extended with an optional children field.
Props extended with a ref field.
Roblox GUI property types and style properties. Typical values include Size, Position, BackgroundColor3, TextColor3, LayoutOrder, etc.
Classname input accepted by helpers such as tw() and cn().
const c: React.ClassName = "flex items-center";
const d: React.ClassName = ["px-4", condition && "bg-blue-500"];Base class component. Supports lifecycle methods and state.
class Counter extends React.Component<{ initialValue?: number }, { count: number }> {
state = { count: this.props.initialValue ?? 0 };
render() {
return <button onClick={() => this.setState({ count: this.state.count + 1 })}>{this.state.count}</button>;
}
}Pure class component with shallow prop/state comparison before re-render.
Higher-order component that memoizes a component for shallow-equality prop checks.
const MemoButton = React.memo(function Button(props: { label: string }) {
return <button>{props.label}</button>;
}, (prev, next) => prev.label === next.label);Clones an existing React element and merges new props.
const next = React.cloneElement(<button className="bg-gray-200" />, {
className: "bg-blue-500",
children: "Save",
});Type guard for React element values.
const ok = React.isValidElement(<div />);Utilities for working with children.
React.Children.map(children, child => child);
React.Children.forEach(children, child => {});
React.Children.count(children);
const only = React.Children.only(children);
const arr = React.Children.toArray(children);Creates a ref object with a mutable current field.
const inputRef = React.createRef<TextBox>();Creates a context object.
const ThemeContext = React.createContext({ accent: Color3.fromRGB(59, 130, 246) });Provides a context value to descendants.
<ThemeContext.Provider value={{ accent: Color3.fromRGB(255, 255, 255) }}>
<ThemedButton />
</ThemeContext.Provider>Consumes a context value with a render prop.
<ThemeContext.Consumer>
{value => <button TextColor3={value.accent}>Theme</button>}
</ThemeContext.Consumer>Hook for reading the current context value.
const theme = React.useContext(ThemeContext);// State and lifecycle
useState<S>(initialState: S | (() => S)): [S, (next: S | ((prev: S) => S)) => void]
useReducer<R, A>(reducer: (state: R, action: A) => R, initialState: R): [R, (action: A) => void]
useRef<T>(initialValue?: T): { current: T | undefined }
useEffect(effect: () => void | (() => void), deps?: unknown[]): void
useLayoutEffect(effect: () => void | (() => void), deps?: unknown[]): void
useMemo<T>(factory: () => T, deps: unknown[]): T
useCallback<T extends (...args: any[]) => any>(callback: T, deps: unknown[]): T
useContext<T>(context: React.Context<T>): T
useImperativeHandle<T, R extends T>(ref: { current: T | undefined }, createHandle: () => R, deps?: unknown[]): voidStores local state and triggers rerenders when updated.
Reducer-based state management for complex state transitions.
Keeps mutable values across renders without causing rerenders.
Runs after render; useful for event subscriptions and cleanup.
Runs after DOM/GUI mutations but before paint; useful for layout measurements.
Memoizes expensive calculations based on dependency changes.
Memoizes a callback function for stable identity.
Reads from a context object.
Exposes imperative methods via a ref.
useId(): string
useTransition(): [boolean, (callback: () => void) => void]
useDeferredValue<T>(value: T): T
useSyncExternalStore<T>(
subscribe: (callback: () => void) => () => void,
getSnapshot: () => T,
getServerSnapshot?: () => T,
): T
useInsertionEffect(effect: () => void | (() => void), deps?: unknown[]): void
useEffectEvent<T extends (...args: any[]) => void>(callback: T): T
useOptimistic<T, U>(
initialValue: T,
updateFn?: (state: T, payload: U) => T,
): [T, (payload: U) => void]
use<T>(resource: Promise<T> | { read(): T }): T
useActionState<S, P>(
action: (prevState: S, payload: P) => S | Promise<S>,
initialState: S,
permalink?: string,
): [S, (payload: P) => void, boolean]Generates a stable unique identifier for IDs and accessibility hooks.
Marks work as non-urgent and returns a pending flag plus a transition start function.
Defers expensive updates until the browser/engine is idle.
Synchronizes with external data stores or Roblox runtime state.
Runs before layout effects for style injection and CSS-like work.
Creates a stable event handler that is not recreated as dependencies change.
Applies optimistic UI updates before a background async action resolves.
Reads a Promise or context-like resource with React 19 semantics.
Manages async action results and pending state, similar to React 19 forms.
const [state, submit, pending] = useActionState(
async (previous, payload: { name: string }) => ({
ok: true,
name: payload.name || previous.name,
}),
{ ok: false, name: "" }
);interface SpringConfig {
tension?: number;
friction?: number;
mass?: number;
restVelocity?: number;
restDelta?: number;
}
interface Motion<T = number> {
current(): number;
map<U>(fn: (progress: number) => U): U;
spring(target: number, config?: SpringConfig): void;
snap(value: number): void;
}
useMotion(initialValue?: number): [Motion, (target: number) => void]
useHoverMotion(initialValue?: number): [Motion, (target: number) => void]Creates a spring-driven animated value for Roblox UI props.
const [motion, setMotion] = React.useMotion(0);
useEffect(() => {
setMotion.spring(1, { tension: 200, friction: 22 });
}, []);
<frame BackgroundTransparency={motion.map(v => 1 - v)} />Returns the current motion value.
Maps the motion progress to a Roblox value such as Color3, UDim2, or a numeric property.
Animates toward a target with spring physics.
Imposes an immediate value without spring interpolation.
Compiler hook used by the React compiler to hold memoized values across renders.
const cache = React.useMemoCache(4);This is primarily for React Compiler / Forget integration and should not normally be called by hand unless you are implementing compiler-generated code.
@nrbx/react includes a Tailwind-like utility layer for Roblox GUI props and layout.
// Tagged template utility
const className = tw`flex items-center gap-2 p-4`;
// Conditional join utility
const merged = cn("flex", active && "bg-blue-500", !disabled && "opacity-100");
configureStyles(config: Partial<StyleConfig>): void
defineConfig(config: StyleSystemConfig): void
createStyleSystem(config: StyleSystemConfig): StyleSystem
processClassName(className: string): Record<string, unknown>
resolveArbitraryValue(value: string): unknown
resolveColor(value: string): Color3
resolveGradient(value: string): Record<string, unknown>Tagged template literal for Tailwind-style className generation.
const props = tw`flex items-center p-4 bg-slate-900 text-white rounded-lg`;
return <frame {...props} />;Conditional class joiner. Values are filtered out when falsy, similar to clsx.
className={cn("rounded", active && "bg-blue-500", disabled && "opacity-50")}Override the runtime style config at startup or in a plugin.
Define a complete style system configuration for theme tokens and custom rules.
Create an isolated style system instance without mutating the global config.
Resolves a class string into actual Roblox props and virtual children.
Parses arbitrary values such as h-[40px], [10], or custom CSS-like values into Roblox-friendly numbers or props.
Parses a color token or arbitrary color string into a Color3 object.
Parses gradient values and resolves them to Roblox-compatible gradient props.
Built-in palette names:
-
slate,gray,zinc,neutral,stone -
red,orange,amber,yellow,lime -
green,emerald,teal,cyan,sky -
blue,indigo,violet,purple,fuchsia -
pink,rose
Each palette supports shades from 50 through 950.
className="bg-blue-500 text-slate-100 border-red-300"The default spacing scale follows Tailwind-like values: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 14, 16, 20, 24, 28, 32, 36, 40, 44, 48, 52, 56, 60, 64, 72, 80, 96.
These map to pixel values around the default design system, including 0, 4, 8, 12, 16, ..., 384.
text-xs, text-sm, text-base, text-lg, text-xl,
text-2xl, text-3xl, text-4xl, text-5xl,
text-6xl, text-7xl, text-8xl, text-9xlThis maps to the default Roblox font sizes.
@nrbx/react provides browser-like element aliases that resolve to Roblox GUI class names.
<div /> // Frame
<span /> // TextLabel
<p /> // TextLabel
<h1 /> // TextLabel
<h6 /> // TextLabel
<button /> // TextButton
<a /> // TextButton
<img /> // ImageLabel
<input /> // TextBox
<textarea /> // TextBox
<ul /> // ScrollingFrame
<ol /> // ScrollingFrame
<li /> // TextLabel
<label /> // TextLabel
<form /> // Frame
<section /> // Frame
<header /> // Frame
<footer /> // Frame
<nav /> // Frame
<main /> // Frame
<article /> // Frame
<aside /> // Frame
<table /> // Frame
<tr /> // Frame
<td /> // Frame
<th /> // Frame
<fieldset /> // Frame
<legend /> // TextLabel
<select /> // Custom selector
<option /> // Choice itemYou can override the mapping with setHTMLElementMap or custom default configs when needed.
@nrbx/react ships two experimental, opt-in behavior props for Roblox GUI elements: draggable and resizable. Both are disabled by default, and both are marked experimental — their behavior may change in future releases.
When draggable is true, the element can be moved freely by dragging it. Movement is clamped to the bounds of the element's parent container, so it can never be dragged outside its parent.
<frame
Position={new UDim2(0, 50, 0, 50)}
Size={new UDim2(0, 200, 0, 120)}
draggable
/>When resizable is true, you can tug any of the element's four corners to resize it. Resizing is clamped to the parent container: the element can never grow larger than its parent. A minimum size of 10 pixels is enforced so the element stays usable.
<frame
Position={new UDim2(0, 50, 0, 50)}
Size={new UDim2(0, 200, 0, 120)}
resizable
/>The two props can be combined, and both can be toggled at runtime. Setting a prop back to false stops the corresponding interaction and cleans up its internal listeners.
- Only applies to
GuiObjectinstances (for exampleFrame,TextLabel,TextButton,ImageLabel) — elements that exposePositionandSize. - Only runs on the client; it requires
RunService:IsClient(). - Experimental: the API surface and behavior are not yet stable.
@nrbx/react translates React-style props to Roblox Event tables.
<button
onClick={() => print("clicked")}
onMouseEnter={() => print("hover")}
onInputChanged={(rbx) => print(rbx.Text)}
/>This becomes a Roblox event table structurally equivalent to:
<Event={{
Activated: () => print("clicked"),
MouseEnter: () => print("hover"),
InputChanged: rbx => print(rbx.Text),
}} />onClick -> Activated
onMouseEnter -> MouseEnter
onMouseLeave -> MouseLeave
onMouseButton1Down -> MouseButton1Down
onMouseButton1Up -> MouseButton1Up
onMouseButton2Down -> MouseButton2Down
onMouseButton2Up -> MouseButton2Up
onMouseMoved -> MouseMoved
onMouseWheelForward -> MouseWheelForward
onMouseWheelBackward -> MouseWheelBackward
onInputBegan -> InputBegan
onInputEnded -> InputEnded
onInputChanged -> InputChanged
onTouchTap -> TouchTap
onTouchLongPress -> TouchLongPress
onDragBegin -> DragBegin
onDragMoved -> DragMoved
onDragEnded -> DragEnded
onSelectionGained -> SelectionGained
onSelectionLost -> SelectionLost
onFocused -> Focused
onFocusLost -> FocusLost
onDoubleClick -> MouseButton1Click
onContextAction -> ContextAction@nrbx/react supports web-style form APIs, adapted to Roblox GUI worlds.
<form className="flex flex-col gap-2">
<label>Display name</label>
<input type="text" value={name} onChange={setName} />
<button type="submit">Submit</button>
</form><form><input><select><option><textarea><label><fieldset><legend><button type="submit">
const status = useFormStatus();
// { pending, data, method, action }Returns:
{
pending: boolean;
data: FormData | null;
method: "get" | "post" | string;
action: string | null;
}const [state, dispatch, isPending] = useActionState(
async (prevState, payload: { username: string }) => {
return { ok: true, username: payload.username };
},
{ ok: false, username: "" }
);Signature:
useActionState<S, P>(
action: (prevState: S, payload: P) => S | Promise<S>,
initialState: S,
permalink?: string,
): [state: S, dispatch: (payload: P) => void, isPending: boolean]<ErrorBoundary
fallback={(error) => <textlabel Text={error.message} />}
onError={(error, info) => print(error, info)}
onReset={() => print("reset")}
resetKeys={[value]}
>
<App />
</ErrorBoundary>Props:
interface ErrorBoundaryProps {
children?: React.ReactNode;
fallback?: React.ReactNode | ((error: ReactErrorInfo) => React.ReactNode);
onError?: (error: unknown, info: ReactErrorInfo) => void;
onReset?: () => void;
resetKeys?: unknown[];
}Parses an error message to extract a source file and line number.
parseErrorSource(message: string): { file?: string; line?: number } | undefinedFormats a thrown value into a developer-friendly error object.
formatReactError(error: unknown): {
message: string;
err: unknown;
stack?: string;
sourceFile?: string;
lineNumber?: number;
componentStack?: string;
timestamp: number;
}@nrbx/react-roblox is the renderer for the Roblox runtime.
import React from "@nrbx/react";
import { createRoot, createPortal, flushSync } from "@nrbx/react-roblox";
const root = createRoot(new Instance("ScreenGui"));
root.render(<frame />);
const portal = createPortal(<textlabel Text="Overlay" />, new Instance("ScreenGui"));
flushSync(() => {
setState(123);
});createRoot(container: Instance): {
render(element: React.ReactElement): void;
unmount(): void;
}createPortal(children: React.ReactNode, container: Instance): React.ReactNodeflushSync(callback: () => void): voidcreateBlockingRoot(container: Instance): { render(...): void; unmount(): void }createLegacyRoot(container: Instance): { render(...): void; unmount(): void }@nrbx/react exposes a cooperative scheduling layer for transitions and deferred work.
unstable_scheduleCallback(priority: SchedulerPriority, callback: () => void): Task
unstable_cancelCallback(task: Task): void
unstable_shouldYield(): boolean
unstable_now(): numberImmediate
UserBlocking
Normal
Low
IdleThese map to the scheduler's cooperative work priorities in the Roblox runtime.
-
@nrbx/reactis intentionally React-like, not a 1:1 browser DOM clone. - HTML aliases are convenience shims for common Roblox GUI instances.
- Tailwind utility parsing is deliberately lightweight and designed for Roblox property sets.
-
React.StrictModeis a compatibility surface; it does not enable browser-style concurrency checks on Roblox.
For more examples, see the wiki pages for Getting Started, HTML Elements, Tailwind Class Names, Forms, Error Boundaries, and Hooks.