Skip to content

Input Form

Multi-field modal form — text, numbers, passwords input. Built-in NUI-side validation.

2 min read

Opening (callback)

exports.LastMenu:input(function(form)
    form:title("Create character")
    form:field("First name", { default = "John" })
    form:field("Age", {
        type = "number",
        min  = 18,
        max  = 99,
    })
    form:confirm("Validate", function(values)
        print("First name:", values[1], "Age:", values[2])
    end)
    form:cancel("Cancel")
end)

Blocking opening (coroutine)

Use input_async in a Citizen.CreateThread to block until confirmation:

Citizen.CreateThread(function()
    local values = exports.LastMenu:input_async(function(b)
        b:title("Bank transfer")
        b:field("Recipient", { type = "text",   placeholder = "Player name" })
        b:field("Amount",    { type = "number", min = 1, max = 100000 })
        b:field("Reason",    { type = "text",   maxlen = 50 })
    end)

    if not values then return end  -- cancelled

    TriggerServerEvent('bank:transfer', values[1], values[2], values[3])
end)

See Async API for details on multi-step chaining.

Reusable form

local loginForm = exports.LastMenu:input_build(function(form)
    form:title("Login")
    form:field("Username",   { maxlen = 24 })
    form:field("Password",   { type = "password" })
    form:confirm("Login", function(v) login(v[1], v[2]) end)
    form:cancel("Cancel")
end)

-- Resets fields to their `default` value on each call
loginForm.open()

Builder methods

MethodArgsDescription
form:title(str)stringOptional header displayed above fields
form:field(label, opts)string, tableAdds an input field
form:confirm(label, cb)string, function(values)Main button — values is a 1-indexed array
form:cancel(label, cb?)string, function?Secondary button — optional callback

Field options

OptionTypeDefaultDescription
typestring"text""text" "number" "password" "email"
defaultstring\|number""Pre-filled value
placeholderstringnilPlaceholder text when field is empty
maxlennumbernilmaxlength attribute
minnumbernilMinimum value (numeric fields)
maxnumbernilMaximum value (numeric fields)
patternstringnilJavaScript regex — validated NUI-side on confirmation
pattern_errorstring"Invalid format"Error message shown if pattern doesn’t match

NUI-side validation

Validation runs on confirmation, before triggering the callback. If a field fails, an inline error message is displayed and the callback is not called.

Built-in validations:

  • type = "number" — must be a valid number; min / max are applied
  • pattern — regex applied via new RegExp(pattern).test(value)
-- License plate AA-123-BB
form:field("Plate", {
    maxlen        = 9,
    pattern       = "^[A-Z]{2}-\d{3}-[A-Z]{2}$",
    pattern_error = "Expected format: AA-123-BB",
})

-- Valid email
form:field("Email", {
    pattern       = "^[^@]+@[^@]+\.[^@]+$",
    pattern_error = "Invalid email",
})

Lua note: Lua uses \\ to produce a single \ in the string sent to JavaScript. The JS regex correctly receives \d, \., etc.


Keyboard navigation

KeyAction
Tab / EnterMove to next field
Enter on last fieldTriggers confirmation
EscapeTriggers cancellation (if defined)

Returned values

Values in the confirm callback are automatically cast according to field type:

  • type = "number" → returns a Lua number (or raw string if tonumber() fails)
  • All other types → return string

The modal blocks the stack while open. Pressing Escape triggers the cancel callback if defined.