Layout
Roblox arranges and constrains UI through child instances: UIListLayout, UIPadding, UIAspectRatioConstraint, and friends. Writing them out longhand is verbose, especially the ones whose properties are UDims. Flux's layout helpers are thin, typed wrappers that return those instances, so they drop straight into a children list alongside everything else:
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Flux = require(ReplicatedStorage.Flux)
local new = Flux.new
new "Frame" {
Size = UDim2.fromOffset(300, 0),
AutomaticSize = Enum.AutomaticSize.Y,
Flux.padding(16),
Flux.list { gap = 8 },
new "TextLabel" { Text = "First" },
new "TextLabel" { Text = "Second" },
}Because each helper just returns an instance, it composes with the normal builder: forValue, selectors, and _CLEAN all keep working, and the parent Frame keeps full strict-typed property autocomplete. The helpers are also available under the Flux.Layout namespace.
Conventions
Three rules apply across every helper:
- Bare numbers are offset pixels.
Flux.padding(8)andgap = 8becomeUDim.new(0, 8). Pass aUDimexplicitly if you want scale. - Friendly strings instead of Enums.
direction = "x",align = "center",Flux.flex("fill"): terser than the fullEnum, and still strictly typed. You can pass a rawEnumanywhere a string is accepted. - Any value can be reactive. A
Nodeor a function works wherever a static value does, and binds reactively, soFlux.padding(Flux.safeArea)andFlux.list { gap = animatedGap }just work, and clean up when the instance is destroyed.
padding
Returns a UIPadding. Accepts one length for all sides, a per-side table, or a reactive struct node:
Flux.padding(8) -- all four sides
Flux.padding { x = 16, y = 8 } -- horizontal / vertical
Flux.padding { top = 8, left = 16 } -- only the sides you name
Flux.padding(Flux.safeArea) -- bind a {top,bottom,left,right} nodelist
Returns a UIListLayout (inheriting Flux's SortOrder = LayoutOrder default).
Flux.list {
gap = 8, -- spacing between items (offset px)
direction = "x", -- "x" (horizontal) | "y" (vertical, default)
align = "center", -- cross-axis alignment
justify = "start", -- main-axis alignment (along `direction`)
wraps = true,
}align and justify follow flexbox semantics: align is the cross axis and justify the main axis (the one items flow along). Because their target axis depends on direction, they take the axis-agnostic strings "start" | "center" | "end". To set an axis directly, or to pass a raw Enum, use horizontalAlign / verticalAlign instead.
grid
Returns a UIGridLayout. cell and gap accept a Vector2 or number (offset px) or a UDim2:
Flux.grid {
cell = Vector2.new(100, 100), -- CellSize
gap = 8, -- CellPadding
fill = "x", -- "x" | "y"
align = "center", -- HorizontalAlignment
justify = "center", -- VerticalAlignment
maxCells = 4, -- FillDirectionMaxCells
}Unlike list, grid's align and justify always map to the horizontal and vertical axes respectively (not the direction-relative cross/main pair) since a grid flows along both axes at once.
aspectRatio
Returns a UIAspectRatioConstraint:
Flux.aspectRatio(16 / 9)
Flux.aspectRatio(1, "scale", "height") -- ratio, aspectType, dominantAxisaspectType accepts "fit" / "scale"; dominantAxis accepts "width" / "height".
sizeConstraint
Returns a UISizeConstraint. Either bound may be omitted:
Flux.sizeConstraint(Vector2.new(200, 100), Vector2.new(600, 400))
Flux.sizeConstraint(nil, Vector2.new(600, 400)) -- max onlyPure UIScale-based scaling shrinks everything uniformly; pairing it with a sizeConstraint keeps small screens from collapsing UI below a usable size. See Responsive.
flex
Returns a UIFlexItem for items inside a flex UIListLayout:
new "Frame" {
Flux.list { direction = "x" },
new "TextLabel" { Text = "Fixed" },
new "Frame" { Flux.flex("fill") }, -- "fill" | "grow" | "shrink" | "none"
}NOTE
These helpers create Roblox instances, so they run under Roblox (not headless Luau). A bad friendly string warns and falls back to a sensible default rather than erroring, but in --!strict mode the typed string-literal unions catch the typo before it ever runs.