Forest Logo
search
package_2

loadingspinner

By @biotoxin495

Roblox

Mirrored

LoadingSpinner — A small animated loading spinner module

LoadingSpinner is a small, dependency-free Roblox utility for displaying animated loading spinners over UI elements.

It creates a dimmed overlay inside any GuiObject, fades in a configurable icon, and rotates it until the loading state is cleared. Each target can have one active spinner, and every spinner is represented by a handle that can be hidden or destroyed independently.

Quick example

local ReplicatedStorage = game:GetService("ReplicatedStorage")

local LoadingSpinner = require(ReplicatedStorage.LoadingSpinner)
local spinners = LoadingSpinner.new()

local target = script.Parent
local handle = spinners:Show(target)

task.wait(2)
handle:Hide()

Hide fades the spinner out before cleaning it up. To remove it immediately instead, call handle:Destroy().

🚀 Features

  • Fully standalone and dependency-free
  • Attach a spinner to any GuiObject
  • One active spinner per target
  • Per-instance default configuration
  • Per-spinner configuration overrides
  • Animated fade-in and fade-out
  • Configurable icon, size, color, rotation speed, and direction
  • Optional input blocking
  • Optional show delay to avoid brief loading flashes
  • Optional minimum visible duration
  • Per-spinner handles
  • IsLoading, SetLoading, and HideAll helpers
  • Automatic cleanup when a target is destroyed
  • Optional custom UI factory
  • Strict Luau types

📖 Basic usage

Copy LoadingSpinner.luau into your project and require it from a client script.

LoadingSpinner creates and animates Roblox UI, so it should normally be used from the client.

Configuring defaults

Pass a configuration table to LoadingSpinner.new to define defaults for that controller.

local spinners = LoadingSpinner.new({
	Icon = "rbxassetid://15734250582",
	IconColor = Color3.fromRGB(255, 255, 255),
	IconSize = UDim2.fromOffset(36, 36),

	OverlayColor = Color3.fromRGB(0, 0, 0),
	OverlayTransparency = 0.4,

	SpinDuration = 1.2,
	FadeDuration = 0.15,
	ShowDelay = 0.1,
	MinimumVisibleDuration = 0.2,
})

Every call to Show can override those defaults:

local handle = spinners:Show(script.Parent, {
	IconColor = Color3.fromRGB(80, 180, 255),
	OverlayTransparency = 0.25,
})

State-driven usage

SetLoading is convenient when the spinner follows an existing boolean state.

spinners:SetLoading(container, true)

local success, result = pcall(loadData)

spinners:SetLoading(container, false)

You can query whether a target currently owns an active or pending spinner:

if spinners:IsLoading(container) then
	print("The container is loading")
end

A spinner waiting for its configured ShowDelay still counts as loading.

⚙️ API

Controller

LoadingSpinner.new(defaultConfig?)

Creates a new LoadingSpinner controller. Each controller owns its own configuration and active spinner registry.

local spinners = LoadingSpinner.new()

spinners:Show(target, config?)

Creates and returns a spinner handle for target.

local handle = spinners:Show(frame)

If the target already has a spinner owned by this controller, the old spinner is destroyed before the new one is created.

spinners:Hide(target)

Fades out the spinner associated with target.

spinners:Hide(frame)

If the spinner is still waiting for its ShowDelay, it is cancelled without being shown.

spinners:HideAll()

Fades out every spinner owned by the controller.

spinners:HideAll()

spinners:IsLoading(target)

Returns whether the target has an active or pending spinner.

local isLoading = spinners:IsLoading(frame)

spinners:SetLoading(target, isLoading, config?)

Shows or hides a spinner based on a boolean.

spinners:SetLoading(frame, true, {
	ShowDelay = 0.15,
})

spinners:SetLoading(frame, false)

When isLoading is true, the method returns the active spinner handle. Repeated true updates reuse the current handle instead of restarting its animation. When isLoading is false, the method returns nil.

spinners:Destroy()

Immediately destroys every spinner owned by the controller and makes the controller unusable.

spinners:Destroy()

Spinner handle

handle:Hide()

Fades the spinner out and then destroys it. The configured MinimumVisibleDuration is respected before the fade begins.

handle:Destroy()

Immediately stops all animations, removes the UI, and unregisters the spinner. It is safe to call more than once.

handle:IsActive()

Returns true while the spinner is pending, visible, or fading out.

handle:IsVisible()

Returns true after the spinner UI has been created. This remains true while it is fading out.

handle:GetTarget()

Returns the target GuiObject, or nil after the handle has been destroyed.

handle:GetUI()

Returns the generated overlay Frame, or nil if the spinner has not appeared yet or has already been destroyed.

Complete configuration reference

PropertyTypeDefaultDescription
Namestring"LoadingSpinner"Name assigned to the generated overlay.
ZIndexnumber100Z-index of the overlay. The indicator uses ZIndex + 1.
IconstringIncluded spinner assetImage used by the spinner.
IconColorColor3WhiteColor applied to the spinner image.
IconSizeUDim232 x 32Size of the spinner image.
SpinDurationnumber1.5Seconds required for one complete rotation.
RotationDirectionnumber1Positive values rotate clockwise; negative values rotate counter-clockwise.
OverlayColorColor3BlackColor of the dimming overlay.
OverlayTransparencynumber0.5Visible transparency of the overlay, clamped from 0 to 1.
BlockInputbooleantrueSets the overlay's Active property so it can intercept input.
FadeDurationnumber0.1Fade-in and fade-out duration in seconds.
ShowDelaynumber0Delay before creating the spinner. Useful for avoiding flashes during fast operations.
MinimumVisibleDurationnumber0Minimum time the spinner remains visible before hiding.
CreatefunctionnilOptional custom UI factory.

Negative duration values are treated as 0. SpinDuration is kept above zero so that the rotation tween remains valid.

Avoiding loading flashes

Very fast operations can briefly display and immediately remove a spinner. ShowDelay prevents the spinner from appearing unless the operation lasts long enough.

local spinners = LoadingSpinner.new({
	ShowDelay = 0.15,
	MinimumVisibleDuration = 0.2,
})

With this configuration:

  • Operations shorter than 0.15 seconds do not display a spinner.
  • Once displayed, the spinner remains visible for at least 0.2 seconds.

Custom UI

Use Create when the default overlay structure does not match your project.

The callback receives the resolved configuration and must return:

  • An unparented Frame used as the overlay.
  • An ImageLabel used as the rotating indicator.
local spinners = LoadingSpinner.new({
	Create = function(config)
		local overlay = Instance.new("Frame")
		overlay.Name = config.Name or "LoadingSpinner"
		overlay.Size = UDim2.fromScale(1, 1)
		overlay.BorderSizePixel = 0
		overlay.BackgroundColor3 = config.OverlayColor or Color3.new(0, 0, 0)
		overlay.ZIndex = config.ZIndex or 100

		local indicator = Instance.new("ImageLabel")
		indicator.AnchorPoint = Vector2.new(0.5, 0.5)
		indicator.Position = UDim2.fromScale(0.5, 0.5)
		indicator.Size = config.IconSize or UDim2.fromOffset(32, 32)
		indicator.BackgroundTransparency = 1
		indicator.Image = config.Icon or ""
		indicator.ImageColor3 = config.IconColor or Color3.new(1, 1, 1)
		indicator.ZIndex = (config.ZIndex or 100) + 1
		indicator.Parent = overlay

		return overlay, indicator
	end,
})

LoadingSpinner ensures the indicator belongs to the returned overlay, then manages parenting, rotation, fading, and cleanup after the callback returns.

📝 Notes

  • A spinner is automatically unregistered when its handle is hidden and the fade completes, its handle is destroyed, its target is destroyed, its generated overlay is destroyed externally, or its controller is destroyed.
  • All cleanup methods are idempotent, so repeated calls are safe.
  • LoadingSpinner is intended for client-side UI.

🛠️ Installation

Copy LoadingSpinner.luau into your project — for example under ReplicatedStorage:

ReplicatedStorage
└── LoadingSpinner

Then require it from a client script:

local ReplicatedStorage = game:GetService("ReplicatedStorage")

local LoadingSpinner = require(ReplicatedStorage.LoadingSpinner)

License

Add the license used by your project before publishing the module.

made with ❤️ by biotoxin495

Package Details

Install command (Click to copy)


Version

1.0.4

License

MIT

check_circle

Safe for commercial use

Automated license review — not legal advice.