ox_target: "press E to interact", done right
Most FiveM servers have things players can interact with: NPCs, doors, trunks, registers, ATMs. Before ox_target, building those interactions meant lots of repeated distance checks and prompt code. With ox_target, most of that becomes one small options table. ox_target is an Overextended community resource, the successor to qtarget and bt-target, not an official Cfx.re resource. This lesson teaches its one core pattern slowly, then uses it three different ways.
Build it
Make the resource folder
Inside your server's resources folder, create this folder:
resources/qu_ox_target_interactions
Create the files
Create this exact file layout. This lesson has a client file (the interactions live where the player is) and a server file (giving cash is a server decision):
resources/qu_ox_target_interactions/
fxmanifest.lua
client.lua
server.lua
Write fxmanifest.lua
Open fxmanifest.lua and paste this:
fx_version 'cerulean'
game 'gta5'
client_script 'client.lua'
server_script 'server.lua'
dependencies {
'ox_lib',
'ox_target',
'/onesync'
}
The canonical manifest key is the plural dependencies { ... } with a table. The singular dependency 'ox_target' is tolerated by FiveM, but the plural form is the form you should write and the form you will see in real resources. We list both ox_lib and ox_target because ox_target itself depends on ox_lib being running first. Older tutorials add a lua54 'yes' line here. As of June 2025 that setting is deprecated and ignored, since Lua 5.4 is now the only Lua runtime, so you leave it out.
Write the client code
Open client.lua and paste this:
-- 1. A box zone over the ATM spot at Legion Square.
exports.ox_target:addBoxZone({
coords = vec3(215.76, -810.12, 30.73),
size = vec3(2.0, 2.0, 2.0),
rotation = 0.0,
debug = true,
options = {
{
name = 'qu_atm_use',
icon = 'fa-solid fa-money-bill',
label = 'Use ATM',
distance = 2.0,
canInteract = function(entity, distance)
return distance <= 2.0
end,
onSelect = function()
print('[qu_ox_target_interactions] ATM used')
end
}
}
})
-- 2. A merchant ped: any ped using this model gets a Talk option.
exports.ox_target:addModel('a_m_y_business_03', {
{
name = 'qu_merchant_talk',
icon = 'fa-solid fa-comment',
label = 'Talk to merchant',
distance = 2.5,
onSelect = function()
print('[qu_ox_target_interactions] merchant talked to')
end
}
})
-- 3. A give cash option on every OTHER player ped.
exports.ox_target:addGlobalPlayer({
{
name = 'qu_give_cash',
icon = 'fa-solid fa-hand-holding-dollar',
label = 'Give $100',
distance = 2.0,
onSelect = function(data)
local serverId = GetPlayerServerId(NetworkGetPlayerIndexFromPed(data.entity))
TriggerServerEvent('qu_ox_target_interactions:giveCash', serverId)
end
}
})
Write the server code
Open server.lua and paste this:
local TRANSFER_AMOUNT = 100
local MAX_DISTANCE = 3.0
local lastRequestAt = {}
RegisterNetEvent('qu_ox_target_interactions:giveCash', function(targetId)
local fromId = source
targetId = tonumber(targetId)
if not targetId or targetId == fromId or not GetPlayerName(targetId) then
print(('[qu_ox_target_interactions] rejected invalid target from %s'):format(fromId))
return
end
local now = os.time()
if now - (lastRequestAt[fromId] or 0) < 1 then
print(('[qu_ox_target_interactions] rate-limited %s'):format(fromId))
return
end
lastRequestAt[fromId] = now
local fromPed = GetPlayerPed(fromId)
local targetPed = GetPlayerPed(targetId)
if fromPed == 0 or targetPed == 0 then return end
local distance = #(GetEntityCoords(fromPed) - GetEntityCoords(targetPed))
if distance > MAX_DISTANCE then
print(('[qu_ox_target_interactions] rejected distant target from %s'):format(fromId))
return
end
-- Proof only. A real transfer must also read the sender's balance on the
-- server and use the installed framework's server-side money functions.
print(('[qu_ox_target_interactions] validated %s -> %s for $%s'):format(
fromId, targetId, TRANSFER_AMOUNT
))
end)
AddEventHandler('playerDropped', function()
lastRequestAt[source] = nil
end)
Start and test it
Open server.cfg and add this line. It must come after the lines that ensure ox_lib and ox_target:
ensure qu_ox_target_interactions
Save, then run:
restart qu_ox_target_interactions
Join the server. You will see a translucent debug box floating at the ATM spot. Walk into it, aim at it, and select Use ATM. Then aim at any other player nearby and select Give $100.
The ATM line prints in F8 because onSelect runs on the client. The give cash line prints in the server console (and txAdmin's Live Console mirrors it) because that work was sent to the server. FiveM also forwards server prints back to the triggering player's F8, so you may see the give cash line there too. The numbers 1 and 2 are server ids, so they will match whoever is actually connected.
Keep reading the full lesson
Sign in to start, then unlock every step of this lesson and the full FiveM School with a membership.
- How it works
- If something went wrong
- What you can do now
- Try it yourself
The remainder of ox_target: interact with anything is available to FiveM School members.