Loom is a pnpm workspace with a Rust half and a TypeScript half. Only two packages are things you install directly; the rest are internal boundaries that happen to be published so the dependency graph resolves.
What you install
| Package | Role |
|---|---|
@loom-dev/preview | The Vite plugin — including the generated page, entry detection, gallery discovery and the gallery shell — plus the browser stand-ins for @rbxts/react-roblox and @rbxts/services, and the globals installer. |
loom-dev | The loom CLI: preview and build, flag/loom.config.ts handling, and the loom-dev/embed and loom-dev/next APIs. A wrapper over the plugin. |
The rest of the scope
| Package | Owns |
|---|---|
@loom-dev/scene | The Scene IR types — the contract every other package is written against. Mirrors the loom-scene Rust crate. |
@loom-dev/layout | A typed async wrapper over the WASM layout engine: initLayout(), computeLayout(scene, viewport). |
@loom-dev/runtime | Roblox datatypes, the Instance model, signals, services, the scheduler, and the Luau standard library. |
@loom-dev/renderer | Scene IR → DOM, plus the incremental patching session with pointer and keyboard input delegation, and the font registry (registerFont, onFontsChanged, clearRegisteredFonts, and familyIsAvailable since 0.9.6). |
@loom-dev/react | The @rbxts/react adapter — a custom react-reconciler host config driving live loom instances. |
@loom-dev/vide | The vide signals adapter, on the same Scene IR. |
Secondary entry points
| Specifier | What it is |
|---|---|
@loom-dev/preview/vite | The Vite plugin. Always import it by this bare specifier — a file path moves the anchor it resolves its aliases from. |
@loom-dev/preview/globals | The Roblox datatype globals, as a side effect. Only needed on a hand-rolled build the plugin cannot see. |
@loom-dev/renderer/fonts | The openly licensed Roblox typefaces, registered on import. The preview imports it for you as of 0.9.0 — it is a separate entry so a project embedding the adapters directly can choose, since the font packages sit behind it. See Fonts and text metrics. |
loom-dev/embed | createGalleryServer(), buildGallery(), findGalleryTargets() — the CLI’s two pipelines as functions. |
loom-dev/next | The Next.js config wrapper around the same two pipelines. |
The Rust half
| Crate | Owns |
|---|---|
loom-scene | Scene IR types and schema — the single source of truth @loom-dev/scene mirrors. |
loom-layout | The layout engine. |
loom-layout-wasm | A wasm-bindgen wrapper around loom-layout, built with wasm-pack --target web. |
The build emits into packages/layout/pkg/, which @loom-dev/layout imports directly. Consumers
receive that binary prebuilt and never need a Rust toolchain.
Dependency direction
loom-dev ──► @loom-dev/preview ──► @loom-dev/react ──► @loom-dev/renderer ──► @loom-dev/scene │ │ └──► @loom-dev/runtime└──► @loom-dev/layout ──► pkg/*.wasmNothing below @loom-dev/react knows a frontend framework exists. That is what let the vide
adapter drop in beside the React one without changes to the layout engine or the renderer, and it is
the intended extension point for a future Luau frontend.
The gallery shell lives in @loom-dev/preview, not the CLI: gallery mode is a plugin option
(loomPreview({ targets })), and the CLI is one of its callers rather than its owner. loom-dev
keeps only what is genuinely command-shaped — argument parsing, loom.config.ts, and the embed API.
Versioning
All published @loom-dev/* packages move together — changesets marks them fixed, so a bump to one
bumps them all. The workspace is a single coherent runtime, and mismatched versions across the scope
are never a supported combination.
Example apps in the repo
Useful as copy-paste references when integrating:
| App | Shows |
|---|---|
apps/example | The minimal roblox-ts client entry the CLI auto-detects — no config of any kind. |
apps/interactive | A vite.config.ts holding nothing but loomPreview(), and no index.html: the plugin-only path, plus input and signal wiring. |
apps/gallery-demo | loomPreview({ targets }) over *.loom.tsx scenes, including one that throws on purpose to exercise error containment. |
apps/playground | The same plugin with a hand-written index.html of its own. |
apps/vide-example | The same pipeline driven by vide instead of React. |