Loomhome

Loom

Render a roblox-ts UI tree as a live web DOM preview, driven by Vite.

loom · v0.11.0 · pre-1.0

Loom takes the @rbxts/react (or vide) UI tree you already ship to Roblox and renders it in a browser, with Roblox layout semantics rather than CSS ones. UIListLayout, UIPadding, AutomaticSize, UISizeConstraint and text measurement are computed by a Rust layout engine compiled to WASM; the result is painted into plain DOM nodes. Clicks, keyboard input and hover route back through UserInputService and instance signals, so a preview is interactive, not a screenshot.

The delivery mechanism is Vite. A single plugin — loomPreview() — aliases @rbxts/react, @rbxts/react-roblox and @rbxts/services onto browser adapters, installs the Roblox datatype globals (UDim2, Color3, Enum, game) and the Luau standard library before your entry evaluates, rewrites roblox-ts import X = require(...) statements to ESM, and resolves roblox-ts packages whose main points at uncompiled Luau straight to their TypeScript source. There is no roblox-ts build step in the loop: esbuild transpiles the same TSX, and HMR works.

One plugin is the whole setup — it generates the page too, so a roblox-ts source tree needs no index.html and no entry wiring:

vite.config.ts
import { loomPreview } from "@loom-dev/preview/vite";
import { defineConfig } from "vite";
export default defineConfig({ plugins: [loomPreview()] });

Or skip the config file entirely with loom preview . — the CLI is the same plugin with a server around it.

vite.config.ts — the whole integration
import { loomPreview } from "@loom-dev/preview/vite";
import { defineConfig } from "vite";
// Aliases @rbxts/react, @rbxts/react-roblox and @rbxts/services onto the
// loom adapters, installs the Roblox globals, and sets the automatic JSX
// runtime — a roblox-ts source tree then runs unmodified in the browser.
export default defineConfig({
plugins: [loomPreview()],
});
Your source, unmodified
No roblox-ts compile step. esbuild transpiles the same TSX Studio gets, and a Vite plugin swaps @rbxts/* for browser adapters.
Roblox-accurate layout
UIListLayout, constraints, AutomaticSize and text measurement run in a Rust layout engine compiled to WASM — not in CSS flexbox.
One contract, any frontend
@rbxts/react and vide both lower to the same Scene IR, so the layout engine and DOM renderer never learn about a framework.

Guides

Wire it into a project.

Vite integration
Add loomPreview() to your own Vite project — the dependencies, the zero-config default, the options, your own HTML entry, static builds and gallery mode.
Advanced Vite setup
The three things that bite once loomPreview() is in a real workspace — import specifiers, unbuilt source checkouts, and sharing a Vite config with a React app.
Gallery targets
Browse many scenes at once with *.loom.tsx targets — the preview export contract, discovery globs, loom.config.ts, and per-target error containment.
Animation
Three ways to move a preview — bindings, TweenService, and the @rbxts/ripple springs — and why an animation costs zero React renders.
Fonts and text metrics
The preview loads the engine's own typefaces and measures text the way the engine does — which families ship, how to register the proprietary ones, why TextSize is not a font size, and where a line breaks.
The debug panel
What a preview is actually doing — the mounted target's timings, the logical viewport, the live instance tree, which typefaces really loaded, and a stage inspector that reaches click-through frames.
Static builds and embedding
Bundle a gallery into a hostable SPA with loom build, then deep-link one scene per iframe using the target/chrome/theme URL contract.
Previews on a phone
What a preview does on a 390px screen — the stage is the viewport and the scene reflows, the ?base= opt-out for a wide composition in a narrow frame, pointer coordinate mapping, touch scrolling, and the gallery chrome on a narrow viewport.
Next.js integration
Serve the loom gallery from a Next.js app with withLoomGallery() — a live proxied gallery under next dev, an automatic static build under next build, and scene iframes in any page, Fumadocs included.
TypeScript setup
Make the editor happy without breaking your roblox-ts build — ambient globals, JSX intrinsics, path mappings, and why none of it affects what renders.
Package compatibility
What loom does with the @rbxts packages your tree imports — the automatic source redirect, the built-in adapters, and the shims option for everything else.
Troubleshooting
The failures you will actually hit — blank previews, missing globals, React duplication, resolution errors — and what each one really means.

Reference

Every flag, alias, and export.

A preview tool, not a runtime.

Nothing loom produces ships to Roblox. It renders your UI tree in a browser so you can iterate without a Studio round-trip, and its fidelity is deliberately partial — UIScale, UIPageLayout and UITableLayout are not implemented. Read the scope before you trust a preview as ground truth.

Scope and status →