Skip to content

JSX & React

JSX elements compile directly to React.createElement() calls for react-lua — no separate type packages or tsconfig.json JSX setup required.

  • JSX elements compile to React.createElement() calls
  • HTML elements map to Roblox GUI classes (see Element Mapping)
  • All React hooks: useState, useEffect, useCallback, useContext, useRef, useMemo, useReducer, useImperativeHandle, useLayoutEffect
  • JSX fragments, spread attributes, conditional rendering, .map() in children
  • key and ref prop handling
  • Portals via ReactRoblox.createPortal

JSX props are translated to their Roblox/react-lua equivalents:

TSXLuau
className="card"[React.Tag] = "card"
id="sidebar"Name = "sidebar"
onClick={fn}[React.Event.Activated] = fn
onChange={fn}[React.Change.Text] = fn
ref={r}ref = r
key={k}Table key in children
Roblox-native propsPassthrough

Any Roblox GUI class name (e.g. <Frame>, <ScrollingFrame>, <TextLabel>) can be used directly as a JSX element and is passed through. Default props are only applied when you don’t specify them yourself.

import React, { useState, useCallback } from "react";
export default function Counter({ label }: { label: string }) {
const [count, setCount] = useState(0);
const increment = useCallback(() => setCount((c) => c + 1), []);
return (
<div className="counter">
<h1>{label}</h1>
<span>Count: {count}</span>
<button onClick={increment}>+</button>
</div>
);
}

Compiles to a React.createElement("Frame", …) tree with <h1>TextLabel, <button>TextButton, and onClick[React.Event.Activated]. See the full output on the Getting Started page.