FR Browse scripts (external link)
Documentation centre
Script MrPoulpi UI

Windows and menus

Input, confirmation and choice in MrPoulpi UI: blocking windows, OpenUI, menus split into sections, client actions and secured server actions.

Updated 25/10/2026 6 min read

Three window types: input (input), confirmation (confirm) and choice (choice). Only one window is open at a time; opening another one replaces the first.

While a window is open, the player has the cursor and no longer controls their character. Escape closes the window, Enter submits the input, Tab moves from one button to the next.

Blocking windows

The simplest way: the function waits for the player’s answer and returns it. Call it inside a thread.

CreateThread(function()
    local plate = exports.mrpoulpi_ui:Input({ title = 'Plate', placeholder = '12ABC345', maxLength = 8 })
    if not plate then return end

    if not exports.mrpoulpi_ui:Confirm(('Register plate %s?'):format(plate)) then return end

    local method = exports.mrpoulpi_ui:Choice({
        title = 'Payment',
        options = {
            { id = 'bank', label = 'Bank', variant = 'success' },
            { id = 'cash', label = 'Cash', variant = 'info' }
        }
    })

    if method then
        TriggerServerEvent('garage:buyPlate', plate, method)
    end
end)
Export Returns If the player cancels
Input(data) the typed text, or a number with inputType = 'number' nil
Confirm(data) true if confirmed, false if refused false
Choice(data) the option id, then the full option nil

If the window is closed by another script or replaced by another one, the function returns the cancel value.

Window fields

These fields work for the blocking functions as well as for OpenUI.

Common

Field Default Description
type 'choice' input, confirm or choice. Not needed with Input, Confirm and Choice.
title — Window title.
cursor true false gives focus without showing the cursor.
action — Action called on submit (input and confirmation). See Actions.
server false With action, sends the action to the server.

Every text accepts a shared/locales.lua key instead of text.

Input (input)

Field Default Description
placeholder “Enter a value” Grey text in the empty field.
value empty Prefilled value.
maxLength Config.Input.maxLength Number of characters, never more than Config.Input.maxLength.
inputType 'text' 'number' only accepts numbers and returns a number.
required true false accepts an empty input.

Confirmation (confirm)

Field Default Description
question — Question shown.
confirmLabel “Confirm” Confirm button.
cancelLabel “Cancel” Refuse button.

Choice (choice)

Field Default Description
options — List of buttons (below).
cancel true Adds a button to close.
cancelLabel “Cancel” or “Close” Text of that button.
columns 3 Number of columns (1 to 4) of a menu split into sections.

Each option:

Field Description
id Identifier returned. By default, the position in the list.
label Button text.
variant Colour: success, info, warning, danger or neutral (default).
group Section title. See Menus in sections.
disabled true shows the button greyed out, not clickable.
keepOpen true keeps the window open after the click.
action, server Action triggered by this button.
onSelect Function called on click: onSelect(payload, context). Client only.
ace Server menu only: the option is only shown to players with this ACE.

As soon as an option has a group, the window becomes a menu: options are sorted under one title per group, in the order the groups appear, and the close button moves to the bottom.

exports.mrpoulpi_ui:OpenUI({
    type = 'choice',
    title = 'Garage',
    columns = 2,
    options = {
        { id = 'out', group = 'Vehicles', label = 'Take out', variant = 'success', action = 'garage:out' },
        { id = 'store', group = 'Vehicles', label = 'Store', action = 'garage:store' },
        { id = 'repair', group = 'Services', label = 'Repair', variant = 'warning', action = 'garage:repair' },
        { id = 'wash', group = 'Services', label = 'Wash', disabled = true }
    }
})

With keepOpen = true, the player can click several buttons in a row. Choice only returns on a click on an option without keepOpen, or on closing.

Actions

Instead of waiting for the answer, you can tie an action to a button. It is useful for a menu opened from the server, or reused in several places.

On click, in this order:

  1. the option onSelect, if any;
  2. the client hook Hooks.OnAction(name, payload, context);
  3. the client action registered under that name, if any;
  4. otherwise, if the option has server = true, the action is sent to the server.

The payload is { id = '…' } for a choice and { value = … } for an input or a confirmation. context is the whole window.

Client action

exports.mrpoulpi_ui:RegisterUIAction('garage:out', function(payload, context)
    print('Chosen option:', payload.id)
end)

The action is removed automatically when the resource that registered it stops.

Server action

exports.mrpoulpi_ui:RegisterServerAction('shop:payBank', function(source, payload)
    -- payload.id or payload.value, already checked
end)

RegisterNetEvent('shop:open', function()
    exports.mrpoulpi_ui:OpenUI(source, {
        type = 'choice',
        title = 'Payment',
        options = {
            { id = 'bank', label = 'Bank', variant = 'success', action = 'shop:payBank', server = true },
            { id = 'cash', label = 'Cash', variant = 'info', action = 'shop:payCash', server = true }
        }
    })
end)

The server accepts an action in two cases only:

Case Condition
Action registered with RegisterServerAction Always accepted. With { requirePermission = true } as 3rd argument, restricted to players with the menu permission.
server = true option of the last window opened by the server for that player, without RegisterServerAction Restricted to players with the menu permission, then handled by Hooks.OnServerAction. Valid once, unless the option has keepOpen.

For a window meant for every player, use RegisterServerAction.

Before reaching your callback, each action is also:

  • limited to one every Config.Security.actionCooldown milliseconds per player;
  • refused if its name is longer than 64 characters;
  • cleaned: only id (64 characters at most) and value (text of at most Config.Input.maxLength characters, number or boolean) are passed on;
  • submitted to Hooks.CanUseAction(source, name, payload).

Refusals go through Hooks.Log (unknown_action, action_denied, invalid_payload).

Updating an open window

exports.mrpoulpi_ui:SetUIData({ title = 'Garage (3 vehicles)' })

Given fields replace the old ones; the others are kept. To follow opening and closing from another script, listen to the local events mrpoulpi_ui:opened and mrpoulpi_ui:closed.

Blocking the opening

In client/hooks.lua, Hooks.CanOpen can refuse a window depending on the player’s state:

function Hooks.CanOpen(data)
    if IsEntityDead(PlayerPedId()) then
        return false
    end
    return true
end

Blocking functions then return the cancel value straight away.

Search

Type a term: setting, export, error…