FragmentFragment

Create a Component

Build reusable template-backed renderers with full reactive access.

A Component packages a template Instance and its rendering logic into a reusable unit. Where a Handle is bound to one specific UI path, a Component is cloned on demand and can be used inside any Handle or other Component.

Components receive the calling Handle as a third argument, giving them full access to Effect, Connect, RegisterContainer, stores, contexts, and hooks — exactly the same as the Handle's own renderer.


Add a template to your assets

Place the template Instance you want to clone somewhere accessible in ReplicatedStorage. For example:

ReplicatedStorage/
└── Assets/
    └── UI/
        └── ItemCardTemplate  ← your Frame here

Create the Component module

Add a ModuleScript inside your Components/ folder.

Components/ItemCard.luau
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Fragment = require(ReplicatedStorage.Fragment)

local Component = Fragment.newComponent("ItemCard", {
    "ReplicatedStorage", "Assets", "UI", "ItemCardTemplate"
})

return Component(function(element: Frame, props: {
    Id:    string,
    Name:  string,
    Price: number,
}, handle: any)
    element.Name = props.Id
    element:FindFirstChild("NameLabel").Text  = props.Name
    element:FindFirstChild("PriceLabel").Text = `🪙 {props.Price}`
end)

newComponent takes:

  • name — unique identifier used with Fragment.useComponent
  • path — child names traversed from game root to the template Instance

Register it with the structured loader

Add your Components folder to Fragment.load. Components are loaded after Contexts and Hooks but before Handles, so they can reference anything that came before them.

FragmentLoad.client.luau
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Fragment = require(ReplicatedStorage.Fragment)

Fragment.load({
    Stores     = script.Parent.Stores,
    Contexts   = script.Parent.Contexts,
    Hooks      = script.Parent.Hooks,
    Components = script.Parent.Components, 
    Handles    = script.Parent.Handles,
})

Use it inside a Handle

Call Fragment.useComponent to clone and render the Component, then hand the result to a Container.

Handles/ShopHandle.luau
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Fragment = require(ReplicatedStorage.Fragment)

local Handle = Fragment.newHandle("Shop", { "HUD", "Shop", "MainFrame" })

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)

Adding reactive behaviour

Because the Handle is forwarded to the Component renderer, you can register Effects, Connections, and anything else you would use in a Handle.

Components/ItemCard.luau
return Component(function(element: Frame, props: {
    Id:    string,
    Name:  string,
    Price: number,
}, handle: any)
    element.Name = props.Id
    element:FindFirstChild("NameLabel").Text  = props.Name
    element:FindFirstChild("PriceLabel").Text = `🪙 {props.Price}`

    handle.Connect("Buy_" .. props.Id, element.BuyBtn.Activated, function() 
        ShopService:PurchaseAsync(props.Id) 
    end) 
end)

Use unique connection names

Connections are stored on the Handle, not the Component. When the same Component is used multiple times in the same Handle, include the item's unique ID in the connection name — e.g. "Buy_" .. props.Id — to avoid one slot overwriting another.


Subscribing to a Store

Components/CoinBadge.luau
return Component(function(element, props, handle)
    local store = Fragment.useStore("PlayerData") 
    local data  = store.getData()

    element.Text = `🪙 {data.coins}`
end)

Subscribing to a Context

Components/CloseButton.luau
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)

Using a Hook

Components/ToggleRow.luau
local useToggle = require(path.to.Hooks.useToggle)

return Component(function(element, props, handle)
    local isOn, toggle = useToggle(props.initialState) 

    element.Toggle.Active = isOn()

    handle.Connect("Toggle_" .. props.id, element.Toggle.Activated, function()
        toggle()
    end)
end)

Nested Components and Containers

A Component can register its own Container and nest further Components inside it.

Components/InventorySection.luau
return Component(function(element, props, handle)
    local list = handle.RegisterContainer(element.ItemList) 

    for _, item in ipairs(props.items) do
        local row = Fragment.useComponent("ItemRow", { 
            id   = item.id, 
            name = item.name, 
        }) 
        list.UpsertChild(item.id, row)
    end
end)

For the full Component API see the Component reference.

On this page