Portals

Portals are a special kind of component provided by Roact that enable components to render objects into a separate, non-Roact Instance.

Info

Eventually, there will be a diagram of Roact portals here. For now, just imagine Valve's hit game, Portal.

To create a portal, use the Roact.Portal component with createElement:

local function PartInWorkspace(props)
    return Roact.createElement(Roact.Portal, {
        target = Workspace
    }, {
        SomePart = Roact.createElement("Part", {
            Anchored = true
        })
    })
end

When we create PartInWorkspace, even if it's deep into our Roact tree, a Part Instance named SomePart will be created in Workspace.

Warning

Portals should only be created to objects that aren't managed by Roact!

One particularly good use for portals is full-screen modal dialogs. When we render a modal dialog, we want it to take over the entire screen, but we want components deep in the tree to be able to create them!

local PlayerGui = game:GetService("Players").LocalPlayer.PlayerGui

-- Our Modal component is a standard component, but with a portal at the top!
local function Modal(props)
    return Roact.createElement(Roact.Portal, {
        target = PlayerGui
    }, {
        Modal = Roact.createElement("ScreenGui", {}, {
            Label = Roact.createElement("TextButton", {
                Size = UDim2.new(1, 0, 1, 0),
                Text = "Click me to close!",

                [Roact.Event.Activated] = function()
                    props.onClose()
                end
            })
        })
    })
end

-- A ModalButton contains a little bit of state to decide whether the dialog
-- should be open or not.
local ModalButton = Roact.Component:extend("ModalButton")

function ModalButton:init()
    self.state = {
        dialogOpen = false
    }
end

function ModalButton:render()
    local dialog = nil

    -- If the dialog isn't open, just avoid rendering it.
    if self.state.dialogOpen then
        dialog = Roact.createElement(Modal, {
            onClose = function()
                self:setState({
                    dialogOpen = false
                })
            end
        })
    end

    return Roact.createElement("TextButton", {
        Size = UDim2.new(0, 400, 0, 300),
        Text = "Click me to open modal dialog!",

        [Roact.Event.Activated] = function()
            self:setState({
                dialogOpen = true
            })
        end
    }, {
        -- If `dialog` ends up nil, this line does nothing!
        Dialog = dialog
    })
end