The Three Kinds of Prompt#
Every prompt in Text UI is a key badge and a line of text. What differs is where it is drawn and what creates it:
Kind | Where it is drawn | Created with |
|---|---|---|
On-screen prompt | Fixed to the screen, in one of nine positions. Slides in when shown and out when hidden. |
|
3D text | Floating over a world coordinate — a door, a counter, a locker. |
|
Prompt on a player | Floating over another player and following them as they move. |
|
Prompts step aside on their own: they are hidden while the pause menu is open, while the screen is faded out, and while another script has the mouse cursor — an inventory, a phone, a menu — and come back when it closes.
How 3D Text Behaves#
A 3D prompt has two distances, measured from the player to the point:
Player is | What they see |
|---|---|
Beyond | Nothing. |
Between | A small marker at the point, so they know something is there. |
Within | The key badge and the text. The key works from here. |
Prompts placed less than two metres apart share a single marker from a distance — a door with three options shows one dot, not three overlapping ones — and split into their own labels once the player is in reach. A prompt on a player works the same way, measured to that player.
Several Options at One Place#
When the player is in reach of more than one prompt at once, they become a list:
Key | Does |
|---|---|
↑ / ↓ (arrow keys) | Move the selection. It wraps from the last option back to the first. |
The option's own key | Runs the selected option — and only that one. |
Options can use different keys — E to knock, G to lockpick — but only the selected one responds, so two prompts can never fire with one press. On-screen options from config areas are listed together at the bottom of the screen; 3D options stay at their own spots with the selected one highlighted. In the Cyberpunk style both are gathered into a single dialogue.
Placing Prompts in the Config#
Prompts that never move — a door, a duty point, a job counter — can be placed in shared/config.lua without writing another script. Each entry in Config.Areas is a data table plus the function that runs when its key is pressed:
Config = {
-- ...
Areas = {
{
data = {
type = '3dtext',
coords = vector3(441.8, -982.1, 30.7),
displayDist = 8,
interactDist = 2,
keyNum = 38, -- control id of the key (38 = E)
key = 'E', -- label on the badge
text = 'Go on duty',
job = 'police' -- 'all' for everybody
},
onKeyClick = function()
TriggerServerEvent('my-police:server:toggleDuty')
end
},
{
data = {
type = 'textui',
coords = vector3(452.3, -980.0, 30.7),
dist = 3,
keyNum = 38,
key = 'E',
text = 'Open the armoury',
job = 'police',
canInteract = function()
return not IsPedInAnyVehicle(PlayerPedId(), false)
end
},
onKeyClick = function()
TriggerEvent('my-police:client:openArmoury')
end
}
}
}Field | Used by | Meaning |
|---|---|---|
| both |
|
| both | A |
| 3dtext | Distance at which the marker appears. |
| 3dtext | Distance at which the text shows and the key works. |
| textui | Distance at which the on-screen prompt shows and the key works. |
| both | Control id of the key that runs |
| both | The label on the badge. Always set it, even on a prompt with no key. |
| both | The text. GTA codes and HTML are allowed. |
| both |
|
| both | Optional function. Return |
onKeyClick runs on the player's own client: call your exports, trigger your events or open a menu from there. canInteract is checked on every frame while the player is near the prompt, so keep it light — read a state bag or a local variable rather than asking the server.
Key numbers#
keyNum is the game's control id, and key is only the label shown — keep them matching. The most used ones:
Key | E | F | G | H | Q | R | X | Y |
|---|---|---|---|---|---|---|---|---|
| 38 | 23 | 47 | 74 | 44 | 45 | 73 | 246 |
Screen Position#
The on-screen prompt can sit in nine places. Config.DefaultAlign is used when a script does not pass one, and any script can choose another per call — the third value of displayTextUI.
| Left | Centre | Right |
|---|---|---|---|
Top |
|
|
|
Middle |
|
|
|
Bottom |
|
|
|
A value that is not on this list falls back to the default instead of failing. A list of on-screen options always sits at the bottom centre.
Colours#
The default look is a blue badge with a light running around its border. Config.Colors changes it for every prompt — on screen, in 3D and the distance marker. Each value is plain CSS, so gradients and rgba work:
Config.Colors = {
-- fill of the key badge, the text box and the marker
insideBackground = [[
radial-gradient(
50% 50% at 50% 50%,
#006299 0%,
#00a3ff 100%
)
]],
insideBoxShadow = '0px 0px 12px 0px rgba(0, 163, 255, 0.7)', -- the glow
insideBorder = '4px solid #0190e1', -- the border
backgroundAnimateColor = 'rgba(0, 163, 255, 1)' -- the light running around it
}For a red theme, for example, swap the blues for #7a0010, #ff2e4d and rgba(255, 46, 77, 0.7), then restart qs-textui.
The Cyberpunk Style#
Switch Config.CyberpunkTextUI.enabled on and every prompt is drawn as a Cyberpunk 2077 dialogue: a speaker plate on top, and each option on its own line with a bar and its key beside it.
Config.CyberpunkTextUI = {
enabled = true,
speaker = 'INTERACT',
showKey = true,
align = 'center-left'
}Field | Default | Meaning |
|---|---|---|
|
| Switches every prompt to the Cyberpunk style. |
|
| The name on the plate above the options. |
|
| Shows the key as a badge on the left of each option. |
|
| Where the on-screen dialogue sits when a script does not choose — any of the nine positions above. |
In this style, the 3D prompts within reach are gathered into one dialogue over the place instead of one box each, and on-screen options form the same dialogue at align. Markers seen from a distance keep a small style of their own. It has its own palette, so Config.Colors does not apply to it.
Text Formatting#
The text of any prompt may carry the GTA formatting codes scripts already use with the game's own text. They are turned into colours — lightened a little so they stay readable on the badge:
Code | Effect | Code | Effect |
|---|---|---|---|
| Red |
| Grey |
| Green |
| Dark grey |
| Blue |
| Black |
| Yellow |
| White |
| Orange |
| Bold on / off |
| Purple |
| Italic on / off |
| Pink |
| New line |
|
|
| Back to normal |
exports['qs-textui']:displayTextUI('Buy for ~g~$500~s~ · ~h~VIP~h~ only', 'E')Any other code — ~INPUT_CONTEXT~, ~a~ — is removed instead of shown. The text is also read as HTML, so <b>, <i> or a <span style="..."> work as well.
Because the text is HTML, do not put text typed by other players — a character name, a chat message — into a prompt without escaping
<and>first.



