One VS Code extension that gives React-Luau, Roact, Fusion, and Vide users the same prop hints, hover docs, color pickers, RichText tooling, and asset previews.
I have in recent months switched over to VS Code to design my Roblox UI using Roblox frameworks. Though personally, I’ve noticed that there are a lot of redundant or repetitive tasks which could be automated easily such as prop hints, RichText highlighting, asset previews, autocomplete, and much more. This is why I built Luix, a single extension which creates a consistant UI-authoring intelligence for React-Luau, Roact, Fusion, and Vide out of the box.
Whichever framework you’re writing in, the same hover docs, the same prop completions, the same color picker, the same RichText helpers, the same diagnostics. One extension, every framework.
Why Luix
- Framework-agnostic. Type
e("Frame", { … }),New "Frame" { … },create "Frame" { … }, or Luau backtick template strings; all of them get the same intelligence. - Smart, not noisy. Diagnostics catch real bugs (
ScrollingDirectionon aFrame,TextColortypo, unused declared props, hard-coded prop overrides) and offer one-click fixes. - Visual when it matters. Image previews on hover and in the gutter; full webview editors for gradients and sprite atlases; inline color pickers for every Color3 form; inline SVG previews for
TweenInfo,UIPadding,UICorner,UIStroke. - Refactor-aware. Extract any element tree into a new component file with only the imports it actually uses. Convert between Color3 / UDim2 forms. Wrap selections in containers. Sort props by category on demand or on save.
Features
Prop completion + type-aware snippets
Click to expand
When you type any prop name inside an element table a snippet wired up with tab stops will show up similar to the example below:
e("TextLabel", {
Back| --> BackgroundColor3 = Color3.fromRGB(255, 255, 255),
Size = U| --> Size = UDim2.new(0, 0, 0, 0),
Font| --> FontFace = Font.fromName("Montserrat", Enum.FontWeight.Regular),
Hor| --> HorizontalAlignment = Enum.HorizontalAlignment.,
})
Works identically across all four frameworks. Color3 placeholder format toggleable between fromRGB / fromHex / new / fromHSV via luix.color3.defaultFormat.
Class-name picker the moment you type e("
Click to expand

e("Fr|") --> accept "Frame" → e("Frame", { <cursor> })
New "Sc|" --> accept "ScrollingFrame" → New "ScrollingFrame" { <cursor> }
create `Im|` --> accept "ImageLabel" → create `ImageLabel` { <cursor> }
When the call has no props table yet, accepting also adds , { … } (parens form) or { … } (curried) with the cursor parked inside.
anchor: preset shortcut
Click to expand

I personally position my AnchorPoint’s and Position properties in the sample places and this tool is here to help combat the constant rewriting of the most common 9 positions you like to place the element. Inside any props table just do:
e("Frame", {
anchor:br| --> AnchorPoint = Vector2.new(1, 1),
--> Position = UDim2.fromScale(1, 1),
})
9 presets: tl t tr l c r bl b br. Paired with an auto-detect diagnostic that flags Position = UDim2.fromScale(0.5, 0.5) without a matching anchor — one-click fix to add it.
RichText completion
Click to expand

Typing < inside Text = "…" opens a tag picker — <b>, <i>, <font>, <stroke>, <mark>, <br/>, and more. Attribute completion chains multiple attributes inside <font …>:
Text = "<font color=\"#FF0000\" size=\"24\" weight=\"Bold\">Hello</font>",
^-- typing `<` opens the tag picker
- Auto-closes
</font>when you finish the opening> - Inline color picker on
color="…"values - Warns when
Textcontains tags butRichText = trueisn’t set (one-click fix) - Inner attribute quotes adapt to the outer Lua quote so
"…",'…', and Luau backticks all work without backslash escaping
Roblox custom glyphs
Click to expand
Roblox’s icon set (Robux, Premium, Verified, Roblox Plus) lives in the Unicode private-use area, which VS Code renders as [] boxes. Luix annotates them:
Text = "Cost: ⏹ 1,199" -- inlay hint reads: ⏹ Robux
Type :robux:, :premium:, :verified:, or :roblox-plus: to insert the literal glyph. Add your own via luix.robloxGlyphs.custom:
{ "luix.robloxGlyphs.custom": { "gbp": "£", "shrug": "¯\\_(ツ)_/¯" } }
Image-asset preview
Click to expand
Hover any "rbxassetid://NNNN" → see the actual Roblox CDN thumbnail in the tooltip (resolved via thumbnails.roblox.com, cached per session).
Opt in to gutter previews for inline thumbnails:
Image = "rbxassetid://13868180889", -- thumbnail appears in the gutter
Cache lives in VS Code’s extension storage (or .luix/assetThumbs/ per workspace, with an auto-written .gitignore). Sidebar shows live cache size + a one-click purge.
Gradient editor (new in v1.4.0)
Click to expand
An Edit UIGradient CodeLens above every e("UIGradient", { … }) opens a combined webview: drag triangle stops on the color ramp, drag circles on the transparency curve, slide rotation −180°..180°, and watch the preview square update live. The preview multiplies by the parent element’s BackgroundColor3 so what you see is what Roblox renders.
Standalone ColorSequence.new(…) and NumberSequence.new(…) literals get a focused per-literal editor with the same UX. Default values (white color, zero transparency, zero rotation) are stripped on write-back, so no noise lands in your code.
Output respects luix.color3.defaultFormat (fromRGB / fromHex / new). Hover previews for both sequence types are on by default.
Sprite-rect editor (new in v1.4.0)
Click to expand
An Edit sprite rect CodeLens appears above every e("ImageLabel" | "ImageButton", { … }) whose Image prop is a literal rbxassetid://…. The webview pulls the asset thumbnail, lets you drag the rect with 8 resize handles, and writes ImageRectOffset / ImageRectSize back to your code.
- Aspect auto-detect — reads a sibling
UIAspectRatioConstraintorSize = UDim2.fromOffset(…)to pre-fill the frame aspect, and renders a dashed yellowScaleType.Croppreview inside your selection. - Native dimensions via Open Cloud (opt-in) — set
luix.openCloud.apiKeyto a Roblox API key with thelegacy-asset:managepermission; the editor auto-fetches each asset’s true pixel size once and caches it forever. Without a key you typeSource W/Source Hmanually — also cached per asset. - Scroll-to-zoom canvas (15–300%) with a discoverable zoom bar; rect can exceed source dims (4x cap, overflow highlighted with a dashed amber border).
- “Full image” defaults (
(0,0)offset,(0,0)size) are stripped on apply.
Visual hover previews (new in v1.4.0)
Click to expand
Inline SVG, rendered straight from your literal values — no network, no caching:
TweenInfo.new(...)— 240×140 graph of the easing curve, all 12EasingStyle× 4EasingDirectioncombinations supported, with duration / repeat / reverses / delay summary.e("UIPadding", { … })— box visualisation with the inner content area indented by the configuredPaddingTop/Right/Bottom/Left. Each non-zero side gets its pixel value labeled.e("UICorner", { … })— rounded rectangle at the configuredCornerRadius.e("UIStroke", { … })— sample box with the stroke applied at the configuredThickness/Color/Transparency.
Toggle with luix.hoverPreviews.enabled (on by default).
Sort props by category (new in v1.4.0)
Click to expand
Right-click anywhere inside a props table and you’ll be able to press the Sort props by category button which reorders props into a specified order: Identification → Layout → Style → Visibility → Image → Text → Behavior → Events → Refs → Children → Other.
e("TextLabel", { e("TextLabel", {
TextColor3 = …, Name = "Greeting",
Size = UDim2.fromOffset(…), Size = UDim2.fromOffset(…),
Name = "Greeting", -> BackgroundColor3 = …,
BackgroundColor3 = …, TextColor3 = …,
[React.Event.Activated] = …, Text = "Hello",
Text = "Hello", [React.Event.Activated] = …,
}) })
- Format-on-save —
luix.sortProps.onSave(defaultfalse). When enabled, every props table in the file sorts on save. Off by default so saving a file doesn’t reshape your layout without you wanting it to. - Configurable order —
luix.sortProps.categoryOrder(string array) lets you reorder or remove categories. - Computed-key + comment safe — captures
[React.Event.Activated],[OnEvent "…"],[Children], etc. Tables with--comments are skipped so comments never get detached.
Diagnostics
Click to expand
e("Frame", {
ScrollingDirection = Enum.ScrollingDirection.Y,
-- ~~~~~~~~~~~~~~~~~~ Unknown property `ScrollingDirection` on `Frame`.
-- 💡 Did you mean `Position`?
Size = UDim2.fromScale(1, 1),
Size = UDim2.new(0, 200, 0, 50),
-- ~~~~ Duplicate prop `Size` — the second assignment overwrites the first.
BorderMode = Enum.Font.SourceSans,
-- ~~~~~~~~~~~~~~~~~~~~~ Expects `Enum.BorderMode`, got `Enum.Font`.
Transparency = 1.5,
-- ~~~ Typically in `0..1` — got `1.5`.
})
Plus: missing AnchorPoint, missing RichText = true, TextScaled collapse-to-zero gotcha, deprecated Font = Enum.Font.X, typo’d TextColor, and an optional WCAG-AA contrast check.
Extract-to-component refactor
Click to expand
Right-click any element tree and the option “Luix: Extract to component…” appears which if clicked prompts for a name and then writes a new .luau alongside the source with only the imports the extracted code actually uses:
-- Before -- After (HomeScreen.luau)
local function HomeScreen() local Card = require(script.Parent.Card)
return e("Frame", { … }, { local function HomeScreen()
e("Frame", { -- cursor here return e("Frame", { … }, {
BackgroundColor3 = …, e(Card, {})
Size = UDim2.fromOffset(…), })
}, { end
e("UICorner", { … }),
e("TextLabel", { Text = "" }) -- Card.luau, freshly written:
}) local React = require(Packages.react)
}) local e = React.createElement
end
local function Card(props)
return e("Frame", { … }, { … })
end
return Card
Framework-aware — emits e(Card, {}) for React/Roact and Card {} for Fusion/Vide.
Design tokens (palette, spacing, fonts)
Click to expand
Define tokens once in your settings; surface them as completions:
| Setting | Triggers after | Suggests entries like |
|---|---|---|
luix.palette |
Color3. |
palette.primary → Color3.fromRGB(124, 92, 255) |
luix.spacing |
UDim. |
spacing.md → UDim.new(0, 16) |
luix.fonts |
Font. |
fonts.display → Font.fromName("Gotham", …) |
Plus a Save Color3 to palette code action on any literal so you build the palette as you go.
And more…
- Inlay hints at closing brackets:
}) ▸ Frame (Container)so you can see what just closed ten levels deep. - Outline + breadcrumbs reflect the element tree, not just Lua function structure.
N referencesCodeLens above every component definition.- Color3 format converter — swap between
fromRGB / fromHex / new / fromHSVwith one click. - UDim2 form converter —
new/fromOffset/fromScale(when expressible). - Wrap-in code actions —
Frame/ScrollingFrame/Frame + UIListLayout. - Component hover — hover
e(MyButton, …)and see the inferred prop list + base class. - Wally + Rojo + scaffold actions in a dedicated activity-bar sidebar.
Quick start
- Install from the Marketplace: Cmd/Ctrl + Shift + X → search Luix Roblox → Install.
Or click: marketplace.visualstudio.com/items?itemName=ericplane.luix-roblox - Open a
.lua/.luaufile and start typing inside ane(…)/New "…" {…}/create "…" {…}/`…`call. Suggestions surface automatically. - (Optional) In settings, set
luix.frameworksto only the frameworks you use. Default is all four.
That’s it — zero config required.
Settings worth knowing
{
// Pick which frameworks Luix recognizes
"luix.frameworks": ["react", "roact", "fusion", "vide"],
// Snippet format for Color3 placeholders
"luix.color3.defaultFormat": "fromRGB", // fromRGB | fromHex | new | fromHSV
"luix.richText.defaultColorFormat": "hex", // hex | rgb
// Opt-in: thumbnail previews in the gutter
"luix.imageGutter.enabled": false,
"luix.imageGutter.cacheLocation": "global", // global | workspace
// Visual editors + previews (all on by default — toggle off if undesired)
"luix.gradient.codeLensEnabled": true,
"luix.gradient.previewOnHover": true,
"luix.rectEditor.codeLensEnabled": true,
"luix.hoverPreviews.enabled": true,
"luix.unusedProps.enabled": true,
// Optional: paste a Roblox Open Cloud API key with the `legacy-asset:manage`
// permission to auto-detect sprite source dimensions in the rect editor
"luix.openCloud.apiKey": "",
// Opt-in: sort props by category on every save (the code action is always available)
"luix.sortProps.onSave": false,
// Opt-in: WCAG AA contrast warnings
"luix.contrastWarnings.enabled": false,
// Opt-in: subtree-bloat CodeLens
"luix.frameStatsLens.enabled": false,
// Opt-in: workspace-wide diagnostic tally in the sidebar
"luix.workspaceValidation.enabled": false,
// Opt-in: augment built-in prop list from Roblox's API dump
"luix.useRobloxApiDump": false,
}
Full settings list with descriptions in the README.
Updates
- v1.5.0 — Per-file framework detection (each file only surfaces its own framework’s snippets), active-framework status-bar item +
luix.activeFrameworkoverride, Vide parens-form support (Vide.create("Frame", {…})), full four-framework parity (Roact scaffolds, matching element snippets, state primitives), workspace-wide rename (F2),UDim2.fromScale↔fromOffsetconversion, Vide direct-call completions (Frame({…})), component auto-import, and completion-gating fixes. - v1.4.0 — Gradient editor (ColorSequence + NumberSequence + UIGradient with live preview, rotation, parent-BG-aware mixing), sprite-rect editor (drag-resize on the asset thumbnail, ScaleType. Crop preview, Open Cloud native-dimension auto-detect), visual hover previews for TweenInfo / UIPadding / UICorner / UIStroke, sort-props-by-category code action + opt-in format-on-save, unused-prop diagnostic, per-class prop type overrides (Frame.Style vs GuiButton.Style).
- v1.3.0 — Design tokens (spacing/fonts), Color3-to-palette extractor, frame-stats CodeLens, project diagnostic summary, numeric-range + TextScaled + WCAG-contrast diagnostics, workspace index persistence, optional Roblox API-dump augmentation,
CFrame.Anglessnippets, opt-in class picker on(. - v1.2.0 — Custom-component hover, Color3 picker now recognizes
fromHex+fromHSV, convert-Color3-format action, reference CodeLens, anchor presets, AnchorPoint auto-detect, wrap-in code actions, UDim2 form conversion, image-asset hover, extract-to-component, image gutter previews. - v1.1.0/1.1.1 — RichText tag completion + attribute chaining + auto-close + color picker, Roblox PUA glyph display + custom glyphs, prop-validation diagnostic suite (unknown/duplicate/wrong-enum/overridden), class-name completion in factory string args.
Full changelog: CHANGELOG.md.
Links
Install: VS Code Marketplace
Source / issues: Repository
Docs: README










