Roblox UI Framework

Roblox UI,
done properly.

Roblox UI is usually a mess of global variables and unorganized frames.

SlateUI is a modular, event-driven framework built for developers who care about code quality.

7

Services

0

Global variables

1

require() to start

MenuController.client.luauLuau
1local RouterService = require(ReplicatedStorage.SlateUI.RouterService)
2 
3-- Register every screen once
4RouterService.RegisterView("Menu", "Home", HomeFrame)
5RouterService.RegisterView("Menu", "Settings", SettingsFrame)
6RouterService.RegisterView("Menu", "Store", StoreFrame)
7 
8-- Navigate between them with one call
9RouterService.Navigate("Menu", "Settings")
Three views registered. One line to navigate. No Visible = true anywhere.

Architecture

Under the Hood

Three structural decisions that make SlateUI maintainable at scale — not just in demos.

01
Memory safety

Zero Global Pollution

Every component is a self-contained object returned from a constructor. Event connections are stored internally and torn down in a single .Destroy() call. Nothing leaks into _G, shared, or ReplicatedStorage by default.

-- Create
local slider = InputsService.Slider(frame, cb)

-- Destroy — disconnects everything
slider:Destroy()
02
State management

Router-Driven Navigation

RouterService manages independent navigation groups. Calling Navigate() closes the previous view and opens the next one with a configurable tween. No manual Visible toggling. No z-index fights. No spaghetti.

RouterService.RegisterView("HUD", "Map", mapFrame)
RouterService.Navigate("HUD", "Map")
-- Previous view auto-closes
03
Theming

Single-Source Theme

Color palettes and tween parameters live in one place inside each service module. Changing DefaultColor in ButtonService immediately affects every button in the game. No hunting through LocalScripts for hardcoded RGB values.

-- In ButtonService.luau
local THEMES = {
  primary = {
    DefaultColor = Color3.fromRGB(71, 85, 105),
    HoverColor   = Color3.fromRGB(61, 74, 91),
    -- edit once, applies everywhere
  },
}

Real-world proof

Not just another UI kit.

Here is what the same problem looks like before and after SlateUI. No benchmarks — just code.

TopicStandard Roblox WorkflowSlateUI Workflow
Navigation
Manually toggle .Visible on each frame, no state tracking
RouterService.Navigate() — closes old, opens new, fires callbacks
Animations
Copy-pasted TweenService calls scattered across dozens of scripts
Centralized tween parameters per service, one edit propagates everywhere
Input handling
UserInputService listeners in random LocalScripts, rarely cleaned up
InputsService attaches and disconnects on AncestryChanged automatically
Theming
Hardcoded Color3 values duplicated across every button and frame
THEMES table in ButtonService — one RGB change updates the entire game
Notifications
Custom RemoteEvent per feature, no standard animation or lifetime
NotificationService client/server API, auto-dismiss, 4 built-in statuses
Cleanup
Connections leak; instances stay in memory after the GUI is destroyed
:Destroy() disconnects every connection and removes the frame
Proximity UI
Per-part scripts with duplicated distance checks on every Heartbeat
ProximityService.Init() — tag once, hysteresis + dot-product handled

Show, don't tell

Live components

Every animation is driven by the same logic that runs in your game. Not CSS simulations.

InputsService.Slider42

Smooth Slider

Spring-physics knob. Zero jitter at boundary.

InputsService.Switch
false

Switch Toggle

Instant state flip with layout-animated knob.

ProximityService — BillboardGui
Interact
>8 studs

Proximity UI

Unfurls on approach. Hysteresis prevents flicker.

NotificationService
Settings saved.success

Notifications

Client or server. Four statuses. Auto-dismiss.

Get started

One plugin. Five minutes.

Install the plugin, require the services, and write less Roblox UI code from now on. The docs cover every service with quick-start and advanced examples.