Start typing to search packages!
textsizer
By @biotoxin495
Roblox
Mirrored from WallyTextSizer — Automatic sizing for Roblox UI text
TextSizer, a small, dependency-free Roblox utility module that sizes TextLabel, TextButton, and TextBox instances to fit their rendered text.
Text-driven UI often needs to grow or shrink as content changes. TextSizer handles this for buttons, tabs, badges, tags, counters, inventory labels, localized text, and other content-driven UI elements.
Quick example
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local TextSizer = require(
ReplicatedStorage:WaitForChild("TextSizer")
)
local handle = TextSizer.Attach(script.Parent, {
Axis = Enum.AutomaticSize.X,
Padding = Vector2.new(16, 6),
MinSize = Vector2.new(80, 32),
MaxSize = Vector2.new(320, 64),
})
TextSizer performs an initial resize and continues updating the object whenever its text or relevant text-rendering properties change.
When the object is no longer needed, destroy the returned handle:
handle:Destroy()
🚀 Features
- Measures text without modifying the object
- Resizes on the X, Y, or both axes
- Reactively resizes changing text through
TextSizer.Attach() - Supports
TextLabel,TextButton, andTextBox - Supports minimum and maximum size constraints
- Supports independent horizontal and vertical padding
- Can include an existing
UIPaddingchild - Uses rendered
TextBoundswhen possible - Falls back to
TextService:GetTextBoundsAsync()for width-constrained wrapped text - Accounts for
FontFace, rich text, localization, and line-height changes - Batches rapid updates with deferred scheduling
- Warns and leaves objects unchanged when
TextScaledorAutomaticSizeis active - Fully typed for Luau strict mode
- No external dependencies
🛠️ Installation
Place the TextSizer ModuleScript somewhere accessible to your client code, such as ReplicatedStorage.
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local TextSizer = require(
ReplicatedStorage:WaitForChild("TextSizer")
)
TextSizer is intended primarily for client-rendered Roblox UI.
📖 Basic Usage
Call Resize when a one-time measurement and resize is needed:
TextSizer.Resize(button, {
Axis = Enum.AutomaticSize.X,
Padding = Vector2.new(12, 0),
MinSize = Vector2.new(100, 0),
MaxSize = Vector2.new(280, math.huge),
})
For content that changes over time, call Attach once. The handle immediately resizes the object and continues watching it for relevant changes.
local handle = TextSizer.Attach(descriptionLabel, {
Axis = Enum.AutomaticSize.Y,
Padding = Vector2.new(0, 8),
MinSize = Vector2.new(0, 24),
MaxSize = Vector2.new(math.huge, 300),
})
The handle also destroys itself when its text object is destroyed. Call Destroy when the binding is no longer needed:
handle:Destroy()
Configuration
Measure, Resize, and Attach accept an optional Options table.
TextSizer.Attach(label, {
Axis = Enum.AutomaticSize.XY,
Padding = Vector2.new(12, 8),
MinSize = Vector2.new(80, 32),
MaxSize = Vector2.new(360, 240),
UseUIPadding = true,
})
Axis
Axis: Enum.AutomaticSize?
Determines which axes Resize and Attach manage.
Axis = Enum.AutomaticSize.X
Manages horizontal sizing and preserves the existing vertical UDim value.
Axis = Enum.AutomaticSize.Y
Manages vertical sizing and preserves the existing horizontal UDim value. This is useful for wrapped text whose width is already determined by its layout.
Axis = Enum.AutomaticSize.XY
Manages both axes.
The default value is Enum.AutomaticSize.X. Enum.AutomaticSize.None is not supported.
Padding
Padding: Vector2?
Adds pixel padding around the measured text. The X value is added to both the left and right sides, while the Y value is added to both the top and bottom sides.
The default value is Vector2.zero.
Padding = Vector2.new(16, 8)
MinSize
MinSize: Vector2?
Sets the minimum final pixel size on each axis.
The default value is Vector2.zero.
MinSize = Vector2.new(100, 40)
MaxSize
MaxSize: Vector2?
Sets the maximum final pixel size on each axis.
The default value is infinite on both axes.
When TextWrapped is enabled and the X axis is being sized, a finite MaxSize.X is also used as the width for text measurement.
MaxSize = Vector2.new(320, 240)
UseUIPadding
UseUIPadding: boolean?
Includes the resolved values of the first direct UIPadding child in the measured size.
The default value is false.
TextSizer.Attach(button, {
Axis = Enum.AutomaticSize.X,
UseUIPadding = true,
})
OnResize
OnResize: ((newSize: Vector2, oldSize: Vector2) -> ())?
Runs after a resize changes the applied pixel size. Callback errors are caught and reported as warnings.
TextSizer.Attach(label, {
OnResize = function(newSize, oldSize)
print("Resized from", oldSize, "to", newSize)
end,
})
Supported Text Objects
TextSizer accepts the following Roblox UI classes:
TextLabelTextButtonTextBox
Passing another Instance to Measure, Resize, or Attach raises an error.
Measurement behavior
TextSizer uses the object's rendered TextBounds whenever that result is suitable. This preserves behavior associated with the actual rendered object, including localization and line-height changes.
When wrapped text must be measured against a width that the object does not currently have, TextSizer falls back to TextService:GetTextBoundsAsync() with GetTextBoundsParams. The fallback uses the object's Text, FontFace, TextSize, and RichText properties.
All final dimensions are rounded upward to reduce one-pixel clipping at text boundaries.
The selected Axis affects wrapped-text measurement, but Measure still returns both measured dimensions.
Axis Examples
Fit a button horizontally
local handle = TextSizer.Attach(button, {
Axis = Enum.AutomaticSize.X,
Padding = Vector2.new(18, 0),
MinSize = Vector2.new(120, 44),
MaxSize = Vector2.new(400, 44),
})
The existing Y component of button.Size is preserved.
Grow a wrapped description vertically
label.TextWrapped = true
label.Size = UDim2.fromOffset(320, 20)
local handle = TextSizer.Attach(label, {
Axis = Enum.AutomaticSize.Y,
Padding = Vector2.new(0, 6),
MinSize = Vector2.new(0, 20),
MaxSize = Vector2.new(math.huge, 260),
})
The label retains its current width and grows or shrinks vertically.
Fit a wrapped tooltip within a maximum width
tooltip.TextWrapped = true
local handle = TextSizer.Attach(tooltip, {
Axis = Enum.AutomaticSize.XY,
Padding = Vector2.new(12, 8),
MinSize = Vector2.new(80, 32),
MaxSize = Vector2.new(320, 220),
})
A finite MaxSize.X supplies the width used to measure wrapped text.
Manual Updates
The module automatically responds to supported UI changes when using Attach, but a recalculation can also be requested manually:
handle:Refresh()
Refresh immediately measures and resizes the attached object.
For a one-time measurement without changing the object, use Measure:
local measuredSize = TextSizer.Measure(label, {
Padding = Vector2.new(8, 4),
MaxSize = Vector2.new(300, math.huge),
})
print(measuredSize)
Handle Methods
handle:Refresh() -> Vector2
Immediately measures and resizes the attached object.
handle:SetOptions(options?)
Replaces the current options, reconnects any axis-dependent observers, and schedules a refresh.
handle:SetOptions({
Axis = Enum.AutomaticSize.XY,
Padding = Vector2.new(12, 8),
MaxSize = Vector2.new(360, 240),
})
handle:Destroy()
Disconnects every observer owned by the handle. Calling it more than once is safe.
🛡️ TextScaled and AutomaticSize
TextSizer intentionally does not resize objects while TextScaled is enabled. TextScaled derives the rendered font size from the container size, while TextSizer derives the container size from the text, creating a circular sizing relationship.
TextSizer also does not resize objects whose Roblox AutomaticSize property is active. Running both systems on the same object can cause conflicting size writes.
In either case, TextSizer emits a warning and leaves the object unchanged. The warning is suppressed until the incompatibility changes, preventing repeated warning spam from attached handles.
Recommended setup:
textObject.TextScaled = false
textObject.AutomaticSize = Enum.AutomaticSize.None
UIPadding
Padding is usually the simplest choice. Set UseUIPadding = true when a text object already has a UIPadding child and the measured size should include it.
local padding = Instance.new("UIPadding")
padding.PaddingLeft = UDim.new(0, 12)
padding.PaddingRight = UDim.new(0, 12)
padding.Parent = button
TextSizer.Attach(button, {
Axis = Enum.AutomaticSize.X,
UseUIPadding = true,
})
Scale-based UIPadding values are resolved against the object's current AbsoluteSize. Pixel offsets are generally more predictable for content-driven sizing.
Lifecycle and performance
Attached updates are batched with task.defer(). If several observed properties change in one task cycle, TextSizer performs at most one scheduled refresh for that handle.
Each call to Attach owns its own connections. Store and destroy the returned handle when replacing UI, changing screens, or otherwise ending the binding's lifetime.
Avoid attaching multiple handles to the same object unless they are deliberately coordinated.
⚙️ API Reference
TextSizer.Measure
TextSizer.Measure(
textObject: Instance,
options: Options?
): Vector2
Measures the content and returns the desired pixel size after applying padding and constraints. It does not modify the instance.
TextSizer.Resize
TextSizer.Resize(
textObject: Instance,
options: Options?
): Vector2
Measures the object and applies the result to the selected axes of its Size property. Selected axes are written as pixel offsets; axes not selected by Axis retain their existing UDim values.
TextSizer.Attach
TextSizer.Attach(
textObject: Instance,
options: Options?
): Handle
Creates a reactive binding and performs an initial resize.
The binding observes changes to:
TextTextSizeFontFaceRichTextTextWrappedLineHeightTextBoundsTextScaledAutomaticSize
For Y-only sizing, it also watches AbsoluteSize so changes to the available width can update wrapped text height.
Complete Example
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local TextSizer = require(
ReplicatedStorage:WaitForChild("TextSizer")
)
local label = script.Parent:WaitForChild("Description")
label.TextWrapped = true
label.Size = UDim2.fromOffset(320, 20)
local handle = TextSizer.Attach(label, {
Axis = Enum.AutomaticSize.Y,
Padding = Vector2.new(0, 8),
MinSize = Vector2.new(0, 24),
MaxSize = Vector2.new(math.huge, 300),
})
task.delay(2, function()
handle:SetOptions({
Axis = Enum.AutomaticSize.XY,
MaxSize = Vector2.new(360, 300),
})
end)
📝 Notes
- TextSizer is designed primarily for client-rendered Roblox UI.
- For the most predictable initial measurement, parent the object into its intended GUI hierarchy before calling
ResizeorAttach. TextBoundsandAbsoluteSizeare most meaningful after the object participates in client layout and rendering.TextSizer.Measurereturns both dimensions even whenAxisselects only one axis.- Selected axes are applied as pixel offsets; unselected axes retain their existing
UDimvalues. - Attached updates are deferred and coalesced per handle.
- Avoid attaching multiple handles to the same object unless they are deliberately coordinated.
Roblox API references
License
No license has been selected in this package. Add the license you want to publish under before releasing the repository.
made with ❤️ by biotoxin495
Package Details
Install command (Click to copy)
Version
1.0.0
License
MIT
Safe for commercial use
Automated license review — not legal advice.
