# esx_context (/docs/core/esx_context)

esx_context is the mouse-driven context menu of ESX Legacy. Use it for short
action lists (an interaction menu, a boss menu) and small forms with text,
number or radio inputs. You open it from client code with `ESX.OpenContext`,
which is a wrapper around the `esx_context` exports.

## Overview [#overview]

| | |
| --- | --- |
| Resource | `esx_context` |
| Side | Client only |
| Dependencies | `es_extended` |
| UI | Plain HTML page (`index.html`), Font Awesome icons |
| Version | 1.16.x (ships with ESX Legacy 1.16.4) |

The menu is a single panel. Only one context menu can be open at a time; opening
a new one replaces the current one.

## Configuration [#configuration]

`config.lua` contains a single global:

| Key | Default | Description |
| --- | --- | --- |
| `PREVIEW_KEYBIND` | `"LMENU"` (left Alt) | Default key for the `previewContext` key mapping, which gives mouse focus to a menu opened in preview mode. Set it to `nil` to skip registering the command and key mapping. |

Players can rebind the key under **Settings > Key Bindings > FiveM** ("Preview
Active Context").

Debug commands (see [Commands](#commands)) are only registered when
`Config.EnableDebug` in es_extended is `true`.

## Functions [#functions]

All functions are client side and available on the `ESX` object once you load
`@es_extended/imports.lua` or call `exports['es_extended']:getSharedObject()`.
Every wrapper returns `false` when `esx_context` is not running.

### ESX.OpenContext [#esxopencontext]

Opens a context menu and gives it mouse and keyboard focus.

```lua
ESX.OpenContext(position, elements, onSelect, onClose, canClose)
```

| Name | Type | Description |
| --- | --- | --- |
| `position` | `string` | `"left"`, `"center"` or `"right"` (default `"right"`). |
| `elements` | `table` | Array of [elements](#element-fields). |
| `onSelect` | `function(menu, element)?` | Runs when the player clicks a selectable element. |
| `onClose` | `function(menu)?` | Runs after the menu closes, by key or by `ESX.CloseContext`. |
| `canClose` | `boolean?` | `false` blocks closing with Escape/Backspace. Defaults to `true`. |

The `menu` argument is the internal menu table: `menu.eles` holds the current
elements (including updated `inputValue`s), `menu.position` the position.

Before the elements reach the UI, `ESX.OpenContext` strips HTML tags from
`title`, `description`, `label` and `inputPlaceholder`. Use plain text.

```lua title="client/main.lua"
local function openGarageMenu()
    local elements = {
        { unselectable = true, icon = 'fas fa-warehouse', title = 'Garage' },
        { icon = 'fas fa-car', title = 'Take out vehicle', name = 'take_out' },
        { icon = 'fas fa-square-parking', title = 'Park vehicle', name = 'park' },
        { disabled = true, icon = 'fas fa-wrench', title = 'Repair', description = 'Mechanics only' },
    }

    ESX.OpenContext('right', elements, function(menu, element)
        if element.name == 'take_out' then
            TriggerServerEvent('my_garage:takeOut')
        elseif element.name == 'park' then
            TriggerServerEvent('my_garage:park')
        end

        ESX.CloseContext()
    end)
end

RegisterCommand('garage', openGarageMenu, false)
```

### ESX.PreviewContext [#esxpreviewcontext]

Shows the menu without taking mouse focus. The player keeps control of the
character and can press the preview key (left Alt by default) to focus the menu
and click it.

```lua
ESX.PreviewContext(position, elements, onSelect, onClose, canClose)
```

The parameters are the same as `ESX.OpenContext`. The preview key only works
when an `onSelect` function was passed.

<Callout type="warn">
`ESX.PreviewContext` passes elements to the UI without the HTML stripping that
`ESX.OpenContext` and `ESX.RefreshContext` apply.
</Callout>

### ESX.CloseContext [#esxclosecontext]

Closes the open menu, releases focus and calls the `onClose` callback. Does
nothing if no menu is open.

```lua
ESX.CloseContext()
```

### ESX.RefreshContext [#esxrefreshcontext]

Replaces the elements and/or the position of the open menu without closing it.
Callbacks and `canClose` stay the same.

```lua
ESX.RefreshContext(elements, position)
```

| Name | Type | Description |
| --- | --- | --- |
| `elements` | `table?` | New element list. `nil` keeps the current elements. |
| `position` | `string?` | New position. `nil` keeps the current position. |

```lua
-- Update a live counter in the open menu
local elements = {
    { unselectable = true, icon = 'fas fa-users', title = 'Players online' },
    { icon = 'fas fa-hashtag', title = tostring(#GetActivePlayers()) },
}

ESX.RefreshContext(elements)
```

## Element fields [#element-fields]

| Field | Type | Description |
| --- | --- | --- |
| `title` | `string` | Main text of the row. |
| `description` | `string?` | Smaller text below the title. |
| `icon` | `string?` | Font Awesome 6 free class, e.g. `"fas fa-check"`. Use `""` for no icon. |
| `name` | `string?` | Your identifier. Use it in `onSelect` and to read input values. |
| `unselectable` | `boolean?` | Rendered as a header row, not clickable. |
| `disabled` | `boolean?` | Rendered grayed out, not clickable. |
| `input` | `boolean?` | Turns the row into an input field. Input rows never trigger `onSelect`. |
| `inputType` | `"text"` \| `"number"` \| `"radio"` | Type of input. |
| `inputPlaceholder` | `string?` | Placeholder text. For radio inputs it also acts as the preselected value when `inputValue` is not set. |
| `inputValue` | `string` \| `number` | Initial value. Updated with what the player enters. |
| `inputMin` | `number?` | Lower bound for `number` inputs (clamped in the UI and in Lua). |
| `inputMax` | `number?` | Upper bound for `number` inputs. |
| `inputValues` | `table?` | Required for `radio`: array of `{ text = 'Label', value = 'key' }` options. |

Any other field you add (for example `value` or `job`) is kept on the element
and passed back to `onSelect`.

### Form example [#form-example]

Input values are written back to `menu.eles` when the field changes (on blur or
Enter for text, on change for number and radio). Read them when the player
clicks a submit row:

```lua title="client/main.lua"
local function openNameForm()
    local elements = {
        { unselectable = true, icon = 'fas fa-id-card', title = 'Register vehicle' },
        { icon = '', title = 'Plate text', input = true, inputType = 'text', inputPlaceholder = 'ABC 123', name = 'plate' },
        { icon = '', title = 'Seats', input = true, inputType = 'number', inputValue = 2, inputMin = 1, inputMax = 8, name = 'seats' },
        {
            icon = '', title = 'Engine', input = true, inputType = 'radio', name = 'engine', inputValue = 'petrol',
            inputValues = {
                { text = 'Petrol', value = 'petrol' },
                { text = 'Electric', value = 'electric' },
            },
        },
        { icon = 'fas fa-check', title = 'Submit', name = 'submit' },
    }

    ESX.OpenContext('center', elements, function(menu, element)
        if element.name ~= 'submit' then return end

        local values = {}
        for _, ele in ipairs(menu.eles) do
            if ele.input then
                values[ele.name] = ele.inputValue
            end
        end

        if not values.plate or values.plate == '' then
            ESX.ShowNotification('Enter a plate first', 'error')
            return
        end

        TriggerServerEvent('my_registry:register', values)
        ESX.CloseContext()
    end)
end
```

<Callout type="error" title="Validate on the server">
Everything in `values` comes from the client. Check lengths, ranges and
permissions again in your server event.
</Callout>

## Exports [#exports]

The `ESX.*` wrappers call these exports. You can use them directly from any
client script.

| Export | Signature | Notes |
| --- | --- | --- |
| `Open` | `exports.esx_context:Open(position, elements, onSelect, onClose, canClose)` | Opens with focus. No HTML stripping when called directly. |
| `Preview` | `exports.esx_context:Preview(position, elements, onSelect, onClose, canClose)` | Opens without focus. |
| `Close` | `exports.esx_context:Close()` | Closes and calls `onClose`. |
| `Refresh` | `exports.esx_context:Refresh(elements, position)` | Updates the open menu. |
| `focusPreview` | `exports.esx_context:focusPreview()` | Gives focus to a previewed menu (same as the preview key). |

## State bag [#state-bag]

While a menu is open, `LocalPlayer.state['context:active']` is `true`. Other
resources can check it to avoid opening UI on top of the menu:

```lua
if LocalPlayer.state['context:active'] then
    return
end
```

## Controls [#controls]

| Key | Action |
| --- | --- |
| Mouse click | Select an element |
| Escape / Backspace | Close the menu (ignored while typing in an input, and when `canClose` is `false`) |
| Left Alt (`previewContext`) | Focus a menu opened with `ESX.PreviewContext` |

## Commands [#commands]

| Command | Description |
| --- | --- |
| `previewContext` | Bound to `PREVIEW_KEYBIND`; focuses a previewed menu. |
| `ctx:preview`, `ctx:open`, `ctx:close`, `ctx:form` | Debug commands that open sample menus. Only registered when `Config.EnableDebug = true` in es_extended. |

## Common issues [#common-issues]

- **`ESX.OpenContext` returns `false` and nothing shows**: `esx_context` is not
  started, or it starts after the resource calling it. Ensure it in
  `server.cfg` after `es_extended`.
- **Argument order**: `onSelect` and `onClose` come before `canClose`, and
  `RefreshContext` takes `elements` first. Older snippets get both wrong.
- **Clicking an input row does nothing**: by design. Add a separate submit row.
- **HTML in titles disappears**: `ESX.OpenContext` strips tags on purpose.
- **Menu stays open after selecting**: selecting does not close the menu. Call
  `ESX.CloseContext()` in `onSelect` when you are done.
- **Mouse cursor stuck after a script error**: if your `onSelect` errors before
  closing, focus stays on the UI. Press Escape, or wrap risky code and always
  call `ESX.CloseContext()`.

## Related [#related]

- [Client UI functions](/docs/es_extended/client/ui)
- [esx_menu_default](/docs/core/esx_menu_default) for keyboard-driven list menus
- [esx_menu_dialog](/docs/core/esx_menu_dialog) for single text input
- [Core resources overview](/docs/core)
