Forest Logo
search
package_2

modalkit

By @biotoxin495

Roblox

Mirrored from Wally

ModalKit — Runtime-generated modal prompts for Roblox

ModalKit, a lightweight, standalone utility for displaying confirmations, alerts, and custom action prompts in Roblox.

ModalKit builds its interface at runtime, so it does not require a prebuilt GUI hierarchy, framework, or third-party runtime dependency. One action-based prompt system powers confirmations, alerts, destructive warnings, multi-choice dialogs, loading states, and custom modal content.

Quick example

ModalKit creates a manager and opens a prompt through a small client-side API.

local ReplicatedStorage = game:GetService("ReplicatedStorage")
local ModalKit = require(ReplicatedStorage.ModalKit)

local prompts = ModalKit.new()

local confirmed, result = prompts:ConfirmAsync({
    Title = "Delete item?",
    Message = "This action cannot be undone.",
    ConfirmText = "Delete",
    Destructive = true,
})

if confirmed then
    print("Delete the item")
else
    print("Cancelled or dismissed:", result.Reason)
end

🚀 Features

Runtime-generated UI

ModalKit creates its own ScreenGui and prompt hierarchy with Instance.new. No prebuilt GUI structure is required.

Action-based prompts

Use Confirm and Alert for common cases, or Open for arbitrary actions with custom IDs, styles, callbacks, and result flags.

Flexible prompt lifecycle

Every prompt returns a handle with Await, Update, Respond, Close, and Destroy methods, plus completion and action signals.

Queueing and overlap policies

Prompts can be queued, replace the active prompt, or be rejected while another prompt is open. Policies can be selected globally or per prompt.

Input and dismissal support

Buttons use Activated, covering mouse, touch, and gamepad input. Enter activates the default action, while Escape, gamepad B, the close button, and the backdrop can dismiss a prompt when enabled.

Themes and custom content

Use global or per-prompt theme overrides, built-in action styles, custom named styles, and runtime-generated content builders with cleanup support.

Fully typed, no dependencies

The module uses strict Luau and has no external runtime dependencies or framework requirements.

📖 Basic usage

Place the ModalKit ModuleScript somewhere accessible to client scripts, such as ReplicatedStorage. ModalKit must be required and used from a LocalScript or another client ModuleScript.

local ReplicatedStorage = game:GetService("ReplicatedStorage")

local ModalKit = require(ReplicatedStorage.ModalKit)
local prompts = ModalKit.new()

Example: confirmation prompt

Confirm creates a standard cancel/confirm dialog and returns a PromptHandle.

local handle = prompts:Confirm({
    Title = "Leave the match?",
    Message = "Your current progress will be lost.",
    ConfirmText = "Leave",
    CancelText = "Stay",
    Destructive = true,
})

handle.Completed:Connect(function(result)
    if result.Confirmed then
        print("Player confirmed")
    else
        print("Prompt ended:", result.Reason)
    end
end)

Example: alert prompt

Alert creates a single-action prompt.

prompts:Alert({
    Title = "Inventory full",
    Message = "Remove an item before collecting another one.",
    ButtonText = "Got it",
    ButtonStyle = "Primary",
})

Example: arbitrary actions

Use Open when the prompt needs more than a simple confirmation.

local handle = prompts:Open({
    Title = "Unsaved changes",
    Message = "Choose what should happen before leaving.",

    Actions = {
        {
            Id = "cancel",
            Text = "Stay here",
            Style = "Secondary",
            IsCancel = true,
        },
        {
            Id = "discard",
            Text = "Discard",
            Style = "Danger",
        },
        {
            Id = "save",
            Text = "Save and leave",
            Style = "Primary",
            IsDefault = true,
        },
    },
})

local result = handle:Await()
print(result.Action, result.Reason)

⚙️ API

ModalKit.new(config?)

Creates a prompt manager. The manager must be created from a client script.

local prompts = ModalKit.new({
    Parent = playerGui,
    ScreenGuiName = "GameModals",
    ResetOnSpawn = false,
    DefaultPolicy = "Queue",

    Theme = {
        DialogMaxWidth = 620,
    },
})
OptionTypeDefaultDescription
ParentInstance?LocalPlayer.PlayerGuiParent used for the generated ScreenGui. If a ScreenGui is supplied, its parent is used.
ScreenGuiNamestring?"ModalKit"Name of the generated ScreenGui.
ResetOnSpawnboolean?falseWhether the generated ScreenGui resets when the player respawns.
DefaultPolicy"Queue" | "Replace" | "Reject""Queue"Behavior when another prompt is already active.
Themetable?Default themeGlobal theme overrides.

ModalKit creates the ScreenGui lazily when the first prompt opens. It owns and destroys that GUI when the manager is destroyed. A caller-provided ScreenGui is left intact.

prompts:Confirm(options?)

Creates a two-action confirmation prompt and returns a PromptHandle.

OptionTypeDefaultDescription
ConfirmTextstring?"Confirm"Confirm button text.
CancelTextstring?"Cancel"Cancel button text.
ConfirmActionIdstring?"confirm"Result ID for the confirm action.
CancelActionIdstring?"cancel"Result ID for the cancel action.
ConfirmStylestring?"Primary"Confirm button style.
CancelStylestring?"Secondary"Cancel button style.
Destructiveboolean?falseUses the Danger style when no explicit confirm style is supplied.
OnConfirmfunction?Callback for the explicit confirm action.
OnCancelfunction?Callback for the explicit cancel action.

OnCancel is called only when the cancel action itself is activated. Other dismissal paths produce separate result reasons.

prompts:ConfirmAsync(options?)

Yields until a confirmation finishes and returns (confirmed, result).

local confirmed, result = prompts:ConfirmAsync({
    Title = "Reset settings?",
    Message = "Your local preferences will return to their defaults.",
    ConfirmText = "Reset",
    Destructive = true,
})

prompts:Alert(options?)

Creates a single-action prompt and returns a PromptHandle.

OptionTypeDefaultDescription
ActionIdstring?"ok"Result ID for the alert action.
ButtonTextstring?"Okay"Visible button text.
ButtonStylestring?"Primary"Button style.

prompts:Open(options)

Creates a generic action prompt. If no actions are supplied, ModalKit creates a single Okay action.

prompts:CloseAll(reason?)

Completes every queued and active prompt with the supplied reason, or "Programmatic" when no reason is supplied. The manager remains usable afterward.

prompts:Destroy()

Closes remaining prompts with reason "Destroyed", disconnects UI events, destroys manager signals, and removes the generated ScreenGui.

ModalKit.GetDefaultTheme()

Returns a fresh copy of ModalKit's default theme. Pass the copy to ModalKit.new or modify it before using it as a theme override.

local theme = ModalKit.GetDefaultTheme()
theme.DialogMaxWidth = 640

local prompts = ModalKit.new({
    Theme = theme,
})

Prompt handles

All prompt creation methods return a handle representing that request.

MethodDescription
handle:Await()Yields until the prompt finishes and returns its result.
handle:Update(patch)Updates queued or active prompt data.
handle:Respond(actionId)Programmatically activates an action on the active prompt.
handle:Close(reason?)Dismisses an active or queued prompt. The default reason is "Programmatic".
handle:Destroy()Closes an unfinished prompt with "Destroyed" and destroys its signals.
handle:GetState()Returns "Queued", "Opening", "Open", "Closing", or "Closed".
handle:GetResult()Returns the completed PromptResult, if available.
handle:IsOpen()Returns whether the prompt is active.
handle:IsFinished()Returns whether the prompt has completed.
handle.ActionTriggered:Connect(function(actionId, activeHandle)
    print("Action:", actionId)
end)

handle.Completed:Connect(function(result)
    print("Closed because:", result.Reason)
end)

Complete options reference

These options are accepted by Open and can also be used with the helper methods where applicable.

Prompt options

OptionTypeDescription
Titlestring?Header text.
Messagestring?Main wrapped message.
Iconstring?Optional image asset URI.
Actions{Action}?Actions for a generic prompt.
ActionLayout"Auto" | "Horizontal" | "Vertical"Button layout. Auto selects a layout based on the action set.
Policy"Queue" | "Replace" | "Reject"Per-prompt overlap policy.
Dismissboolean | tableEnables or configures non-action dismissal.
Animateboolean?Set to false to disable transitions.
AnimationDurationnumber?Overrides the transition duration.
Loadingboolean?Prevents action activation while true.
Contentfunction?Builds custom body content.
Themetable?Per-prompt theme overrides.
OnOpenfunction?Called after the prompt becomes active.
OnActionfunction?Called as (actionId, handle).
OnClosefunction?Called as (result, handle).

Action fields

{
    Id = "save",
    Text = "Save and leave",
    Style = "Primary",
    IsDefault = true,
    IsConfirm = true,
    AutoClose = true,
}
FieldTypeDefaultDescription
Idstring?Action indexUnique action/result identifier.
Textstring?IdVisible button text.
Stylestring?"Secondary"Theme style name.
IsDefaultboolean?AutomaticDefault keyboard/gamepad action.
IsConfirmboolean?falseMarks its result as confirmed.
IsCancelboolean?falseMarks its result as cancelled.
Disabledboolean?falseDisables interaction with the action.
AutoCloseboolean?trueSet to false to keep the prompt open after activation.
Callbackfunction?Called as Callback(handle, action) after activation.

Action IDs must be unique within a prompt. If no enabled action is explicitly the default, ModalKit selects the last enabled action.

Behavior

Dismissal

All standard dismissal methods are enabled by default.

Dismiss = {
    CloseButton = true,
    Backdrop = true,
    Escape = true,
    GamepadBack = true,
}

Disable every non-action dismissal path with Dismiss = false, or configure the paths individually. Each path produces a distinct Reason.

Prompt overlap policies

When a prompt opens while another is active, Policy determines what happens:

  • "Queue" stores the prompt and opens it after earlier prompts finish. This is the default.
  • "Replace" closes the current prompt with reason "Replaced" and places the new prompt at the front of the queue.
  • "Reject" completes the new handle with reason "Rejected" without displaying it.

Loading and runtime updates

Set an action's AutoClose to false to keep the prompt open while work continues. Use Update to change the message, loading state, actions, content, or theme.

local prompt = prompts:Open({
    Title = "Publish build?",
    Message = "The build will become visible to players.",
    Actions = {
        {
            Id = "publish",
            Text = "Publish",
            Style = "Primary",
            IsDefault = true,
            AutoClose = false,
        },
    },
})

prompt.ActionTriggered:Connect(function(actionId)
    if actionId == "publish" then
        prompt:Update({
            Message = "Publishing…",
            Loading = true,
        })
    end
end)

While a prompt is loading, action responses are ignored.

Lifecycle callbacks and manager signals

OnOpen, OnAction, and OnClose callbacks can be supplied in prompt options. They are protected and run asynchronously so callback errors do not interrupt ModalKit's cleanup.

prompts:Open({
    Title = "Example",
    OnOpen = function(handle)
        print("Opened", handle.Id)
    end,
    OnAction = function(actionId, handle)
        print("Action", actionId, "on", handle.Id)
    end,
    OnClose = function(result, handle)
        print("Closed", handle.Id, result.Reason)
    end,
})

A manager also exposes lifecycle signals for every prompt it owns:

prompts.PromptOpened:Connect(function(handle)
    print("Opened", handle.Id)
end)

prompts.ActionTriggered:Connect(function(handle, actionId)
    print("Action", actionId, "on prompt", handle.Id)
end)

prompts.PromptClosed:Connect(function(handle, result)
    print("Closed", handle.Id, result.Reason)
end)

Custom content

Use Content to insert arbitrary runtime-generated UI into the body of a prompt.

prompts:Confirm({
    Title = "Purchase item?",
    Message = "Review the purchase before continuing.",

    Content = function(container, handle)
        local summary = Instance.new("TextLabel")
        summary.BackgroundTransparency = 1
        summary.AutomaticSize = Enum.AutomaticSize.Y
        summary.Size = UDim2.new(1, 0, 0, 0)
        summary.Text = "Crystal Sword — 500 coins"
        summary.TextWrapped = true
        summary.Parent = container

        return function()
            -- Disconnect custom events or release other resources here.
        end
    end,
})

A content builder may return a cleanup function, an unparented Instance for ModalKit to parent, or nothing when it handles parenting itself. Updating Content rebuilds the section and runs the previous cleanup path first.

Themes

Get a fresh copy of the default theme or provide only the properties to override.

local prompts = ModalKit.new({
    Theme = {
        DialogColor = Color3.fromRGB(246, 247, 251),
        TitleColor = Color3.fromRGB(25, 27, 34),
        MessageColor = Color3.fromRGB(73, 77, 91),
        Styles = {
            Primary = {
                BackgroundColor = Color3.fromRGB(120, 80, 255),
            },
        },
    },
})

Built-in action styles are Primary, Secondary, Danger, Success, and Neutral. Additional named styles can be defined under Theme.Styles and referenced by an action's Style field.

Input behavior

  • Buttons use Activated, covering mouse, touch, and gamepad activation.
  • Enter and keypad Enter trigger the default action.
  • Keyboard activation is ignored while a TextBox is focused.
  • Escape, gamepad B, and backdrop activation dismiss the prompt when enabled.
  • Gamepad selection moves to the default enabled action where applicable.
  • The previously selected GUI object is restored after the modal closes when it still exists.

Prompt results

A completed prompt produces a result table:

{
    Action = "confirm", -- nil when no action was selected
    Reason = "Action",
    Confirmed = true,
    Cancelled = false,
}

Built-in reasons are Action, CloseButton, Backdrop, Escape, GamepadBack, Programmatic, Replaced, Rejected, and Destroyed. Custom strings supplied to Close or CloseAll are preserved. Confirmed and Cancelled follow the action's IsConfirm/IsCancel flags or the conventional IDs "confirm"/"cancel".

📝 Notes

  • ModalKit is a client-side interface utility. A confirmation result communicates user intent, not authorization.
  • Never treat a successful client confirmation as proof that a purchase, trade, deletion, currency change, reward, or administrative action is valid. Validate sensitive operations on the server.
  • The package is a single root ModuleScript with internal modules beneath it:
ReplicatedStorage
└── ModalKit
    ├── Cleaner
    ├── PromptHandle
    ├── PromptView
    ├── SimpleSignal
    └── Theme
  • The accompanying showcase game demonstrates confirmations, alerts, multi-action prompts, custom content, queueing, replacement, loading updates, themes, scrolling, and lifecycle logging.
  • Call prompts:CloseAll() when changing scenes, and prompts:Destroy() when the owning controller or interface is torn down.

🛠️ Installation

Studio model

Import ModalKit.rbxmx into your place and put the resulting ModalKit ModuleScript somewhere accessible to the client, such as ReplicatedStorage.

Then require it from a LocalScript or another client ModuleScript:

local ReplicatedStorage = game:GetService("ReplicatedStorage")
local ModalKit = require(ReplicatedStorage.ModalKit)

Creating a manager from the server will throw an error.

Rojo

Map the ModalKit source into ReplicatedStorage in your project file, then require it from client code as shown above.

License

Use the license included with your ModalKit release or repository.

made with ❤️ by biotoxin495

Package Details

Install command (Click to copy)


Version

1.0.1

License

MIT

check_circle

Safe for commercial use

Automated license review — not legal advice.