Create a Handle
Bind a renderer to a UI Instance and react to state changes.
A Handle is the core unit of Fragment. It resolves a path from PlayerGui down to a specific UI Instance, then re-runs a renderer function whenever its state or subscribed data changes.
Define a Handle
Each Handle lives in its own ModuleScript inside your Handles/ folder.
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Fragment = require(ReplicatedStorage.Fragment)
local Handle = Fragment.newHandle("Shop", { "HUD", "Shop", "MainFrame" })
return Handle(function(element: Frame)
-- renderer body
end)newHandle takes two arguments:
- name: a unique identifier used to retrieve this Handle via
Fragment.getHandle - path: child names traversed from
PlayerGuito reach the target Instance
One Handle per ModuleScript
Each ModuleScript should create and return exactly one Handle. Fragment.load requires every ModuleScript it finds — non-Handle modules like configs or utilities are required and ignored safely.
Local state with bind
Handle:bind creates a reactive value slot inside the renderer. The slot is initialised once on the first render — every subsequent render returns the same getter and setter with no extra allocations.
return Handle(function(element: Frame)
local activeTab, setActiveTab = Handle:bind("General")
label.Text = `Active: {activeTab()}`
Handle.Connect("GeneralTab", generalBtn.Activated, function()
setActiveTab("General")
end)
Handle.Connect("ShopTab", shopBtn.Activated, function()
setActiveTab("Shop")
end)
end)Binding order
Always call bind (and all other bindings like derive, hold, await) at the top of the renderer, unconditionally, in the same order every render. Conditional or reordered bindings will read from the wrong slot.
Side effects with Effect
Handle.Effect registers a side effect that runs after every render. Return a cleanup function to have it called before the next render.
Handle.Effect(function()
local conn = RunService.Heartbeat:Connect(function()
element.Text = os.date("%H:%M:%S")
end)
return function()
conn:Disconnect()
end
end)One-time setup with Once
Handle.Once runs its callback exactly once — on the first render that execution reaches that call.
Handle.Once(function()
for i, item in ipairs(catalogData) do
table.insert(cachedItems, item)
end
end)Named connections with Connect
Handle.Connect wires a signal under a named slot. The previous connection for that name is automatically disconnected on each render.
local closeBtn = element.TopBar.Close :: TextButton
Handle.Connect("Close", closeBtn.Activated, function()
Fragment.getHandle("Shop"):Toggle(false)
end)Named actions
Actions let other parts of your code call into a Handle from outside the renderer. They are registered once and remain callable even when the element is hidden.
Handle.Action("Toggle", function(state: boolean)
element.Visible = state
end)Fragment.getHandle("Shop"):Toggle(true)Dynamic children with RegisterContainer
Handle.RegisterContainer returns a Container that diffs children across renders. It can be called at the top level of a renderer or inside an Effect callback.
return Handle(function(element: Frame)
local store = Fragment.useStore("Shop")
local data = store.getData()
local grid = Handle.RegisterContainer(element.ItemGrid)
for _, item in ipairs(data.items) do
local card = Fragment.useComponent("ItemCard", {
Id = item.id,
Name = item.displayName,
Price = item.price,
})
grid.UpsertChild(item.id, card)
end
end)Full example
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Fragment = require(ReplicatedStorage.Fragment)
local Handle = Fragment.newHandle("Shop", { "HUD", "Shop", "MainFrame" })
return Handle(function(element: Frame)
local activeTab, setActiveTab = Handle:bind("General")
Handle.Action("Toggle", function(state: boolean)
element.Visible = state
end)
local closeBtn = element.TopBar.Close :: TextButton
Handle.Connect("Close", closeBtn.Activated, function()
Fragment.getHandle("Shop"):Toggle(false)
end)
element.Title.Text = `{activeTab()} — Shop`
Handle.Connect("GeneralTab", element.Tabs.General.Activated, function()
setActiveTab("General")
end)
Handle.Connect("FeaturedTab", element.Tabs.Featured.Activated, function()
setActiveTab("Featured")
end)
end)For the full API see the Handle reference.