Create a Context
Shared reactive value objects consumed by any Handle or Component.
A Context is a lightweight shared value object. Any Handle that calls handle:pull(ctx) subscribes to it and re-renders whenever ctx:patch() is called.
Contexts are ideal for cross-Handle state that doesn't belong to a specific data source — like which window is currently open, or which theme is active.
Define a Context
Each Context lives in its own ModuleScript inside your Contexts/ folder.
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Fragment = require(ReplicatedStorage.Fragment)
local WindowContext = Fragment.createContext({
active = nil :: string?,
})
WindowContext._value.open = function(name: string)
WindowContext:patch({ active = name })
end
WindowContext._value.close = function(name: string)
if WindowContext._value.active == name then
WindowContext:patch({ active = Fragment.DELETE })
end
end
WindowContext._value.toggle = function(name: string)
if WindowContext._value.active == name then
WindowContext:patch({ active = Fragment.DELETE })
else
WindowContext:patch({ active = name })
end
end
return WindowContextFragment.DELETE removes the key from the value table entirely when passed to patch. Methods added to _value are available to any Handle or Component that pulls from the Context.
Use the structured loader
When using Fragment.load({ Contexts = ..., Handles = ... }), Contexts are required before Handles automatically — no manual require needed.
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Fragment = require(ReplicatedStorage.Fragment)
Fragment.load({
Contexts = script.Parent.Contexts, -- required before Handles --
Components = script.Parent.Components,
Handles = script.Parent.Handles,
})Connect a Context to a Handle
Use handle:pull(ctx) inside a renderer to subscribe the Handle and get the live value table back. The Handle re-renders whenever ctx:patch() is called.
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Fragment = require(ReplicatedStorage.Fragment)
local WindowContext = require(ReplicatedStorage.Parent.Contexts.WindowContext)
local Handle = Fragment.newHandle("Shop", { "HUD", "Shop", "MainFrame" })
return Handle(function(element: Frame)
local win = Handle:pull(WindowContext)
element.Visible = win.active == "Shop"
Handle.Action("Toggle", function(state: boolean)
if state then
win.open("Shop")
else
win.close("Shop")
end
end)
local closeBtn = element.TopBar.Close :: TextButton
Handle.Connect("Close", closeBtn.Activated, function()
win.close("Shop")
end)
end)Contexts work identically inside Component renderers via the handle argument:
local WindowContext = require(path.to.Contexts.WindowContext)
return Component(function(element, props, handle)
local win = handle:pull(WindowContext)
handle.Connect("Close_" .. props.windowName, element.Activated, function()
win.close(props.windowName)
end)
end)Patching the Context directly
Call ctx:patch(partial) on the Context itself to merge values and notify all subscribed Handles and Components. This is how the methods defined in the Context body work under the hood.
WindowContext:patch({ active = "Shop" }) -- open shop
WindowContext:patch({ active = Fragment.DELETE }) -- close allMultiple Handles sharing the same Context
The real power of Contexts is that any Handle can subscribe independently. Both the window Handle and its opener button can react to the same active value without knowing about each other.
local WindowContext = require(path.to.WindowContext)
local Handle = Fragment.newHandle("ShopOpener", { "HUD", "Buttons", "Shop" })
return Handle(function(element: TextButton)
local win = Handle:pull(WindowContext)
element.BackgroundTransparency = win.active == "Shop" and 0 or 0.5
Handle.Connect("Open", element.Activated, function()
win.toggle("Shop")
end)
end)For the full Context API see the Context reference.