rbx-tsx
v0.4.1
Published
TSX to Luau compiler targeting react-lua for Roblox
Maintainers
Readme
rbx-tsx
TSX/TypeScript → Luau compiler targeting react-lua for Roblox. Write React components in TypeScript/TSX, get clean, fully typed, dependency-free Luau that works inside Roblox.
📖 Documentation · 🛝 Playground
Why rbx-tsx?
Unlike roblox-ts, rbx-tsx compiles to standalone Luau with no runtime dependency and preserves your TypeScript types as Luau type annotations. JSX is built in (no @rbxts/* packages or tsconfig.json JSX setup), HTML elements map to Roblox GUI classes automatically, and JS APIs (console, Math, JSON, Array, RegExp, …) are inlined directly into the output.
See the full comparison in the docs.
Quick Start
npm install rbx-tsx
rbx-tsx init my-app # scaffold a Rojo-ready project
cd my-app
rbx-tsx compile src/ -o out/Example
Input (Counter.tsx):
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>
);
}Output (Counter.luau):
const React = require(game:GetService("ReplicatedStorage").Packages.React)
const useState = React.useState
const useCallback = React.useCallback
const function Counter(props: { label: string })
const label = props.label
const count, setCount = useState(0)
const increment = useCallback(function()
setCount(function(c) return c + 1 end)
end, {})
return React.createElement("Frame", { [React.Tag] = "counter" }, {
H1 = React.createElement("TextLabel", { Text = label }),
Span = React.createElement("TextLabel", { Text = `Count: {count}` }),
Button = React.createElement("TextButton", {
[React.Event.Activated] = increment,
Text = "+",
}),
})
end
return CounterTry it live in the Playground, or browse examples/ for side-by-side input/output pairs (async/await, decorators, generators, optional chaining, regex, Roblox services, types). The demo/ directory is a full Rojo + Wally project.
CLI
rbx-tsx compile <input> # compile a file or directory (stdout or -o <path>)
rbx-tsx watch <path> # recompile on change
rbx-tsx check <input> # type-check without emitting
rbx-tsx init <name> # scaffold a new project
rbx-tsx types [path] # generate .d.ts from wally/pesde packages or local .luau modulesDirectory compilation and watch also copy hand-written .lua/.luau files into the output tree, so -o out/ produces a complete Rojo tree. Copies are verbatim — same relative path and name (the index.luau → init.luau rename applies only to compiled sources; name a hand-written init init.luau directly). .test./.spec. files, .d.luau/.d.lua declarations, and anything inside the output directory are skipped, and a hand-written file that collides with a compiled output is skipped with a warning.
Full flag reference and feature details are in the docs:
- JSX & React — hooks, element & props mapping
- Language & API Transforms
- Module System & Roblox Integration
- Type System & Package Type Extraction
Contributing
The docs site lives in docs-site/ (Astro Starlight) and the in-browser playground in playground/. See CLAUDE.md for the compiler architecture.
