qb-target vs ox_target: convert AddBoxZone and fix targets not showing

Moving from qb-target to ox_target on FiveM: how to convert AddBoxZone and AddTargetModel calls, what the compatibility layer does, and why targets do not show.

You moved from qb-target to ox_target, or you downloaded a script written for one while running the other, and the interaction zones are gone or doubled. Here is how the two differ, how to convert the calls, and how to find why a target does not show.

The difference in one table

Both resources do the same job: a key opens a targeting eye and the player picks an option on an entity or in a zone. They differ in how you describe it.

Idea qb-target ox_target
Box zone AddBoxZone(name, center, length, width, options, targetOptions) addBoxZone({ coords, size, rotation, options })
Model AddTargetModel(models, options) addModel(models, options)
What to do type, event, action onSelect, event, serverEvent
Job limit job groups
Item limit item items
Extra check canInteract canInteract

ox_target takes a single table for zones, and the options are a plain list, with no wrapper that holds options and distance together.

Convert a box zone

A typical qb-target zone:

lua
exports['qb-target']:AddBoxZone('pd_armory', vector3(452.2, -980.0, 30.7), 1.5, 1.0, {
    name = 'pd_armory',
    heading = 90.0,
    debugPoly = false,
    minZ = 29.7,
    maxZ = 31.7,
}, {
    options = {
        {
            type = 'client',
            event = 'my_script:openArmory',
            icon = 'fas fa-gun',
            label = 'Open armory',
            job = 'police',
        },
    },
    distance = 2.0,
})

The same zone in ox_target:

lua
exports.ox_target:addBoxZone({
    coords = vec3(452.2, -980.0, 30.7),
    size = vec3(1.0, 1.5, 2.0),
    rotation = 90.0,
    debug = false,
    options = {
        {
            name = 'pd_armory',
            event = 'my_script:openArmory',
            icon = 'fa-solid fa-gun',
            label = 'Open armory',
            groups = 'police',
            distance = 2.0,
        },
    },
})

What moved:

  • center becomes coords, and it is the centre of the box in both, but the height now comes from size. qb-target uses minZ and maxZ, so put coords.z halfway between them and use their gap as the height.
  • length and width become the x and y of size. If the box ends up turned the wrong way, swap them or change rotation, and check with debug = true.
  • heading becomes rotation.
  • job becomes groups, and distance moves into each option.
  • type = 'client' with event stays an event. Use serverEvent for type = 'server', or onSelect with a function instead of an event.

Convert a model target

qb-target:

lua
exports['qb-target']:AddTargetModel({ `prop_atm_01`, `prop_atm_02` }, {
    options = {
        {
            type = 'client',
            event = 'my_script:openAtm',
            icon = 'fas fa-credit-card',
            label = 'Use ATM',
        },
    },
    distance = 1.5,
})

ox_target:

lua
exports.ox_target:addModel({ `prop_atm_01`, `prop_atm_02` }, {
    {
        name = 'my_script_atm',
        event = 'my_script:openAtm',
        icon = 'fa-solid fa-credit-card',
        label = 'Use ATM',
        distance = 1.5,
    },
})

The options are now the second argument itself, a list of option tables. The full list of exports and fields is in ox_target: addBoxZone, addModel and addLocalEntity.

Keep an old script working

ox_target provides compatibility for many qb-target exports. A script that still calls exports['qb-target']:AddBoxZone(...) can keep working when only ox_target is running, as long as it uses the common exports.

Do not rely on it for everything. Less common exports and unusual option fields may not behave the same. Test each script, and when one misbehaves, convert its calls to the native ox_target exports as shown above. Native calls are also easier to read and debug.

Warning: do not start qb-target next to ox_target to "make sure". Keep one targeting resource only, and remove the other from server.cfg.

Why targets do not show

Almost every "my target is invisible" case falls in this list.

Both resources are running. Two targeting resources fight over the same key. Check server.cfg and your resources list, and keep only one:

cfg
ensure ox_lib
ensure ox_target
# ensure qb-target   <- remove or comment out

The distance is too small. The option shows only inside its distance. Try distance = 3.0 while you test. With qb-target, the distance sits next to options, not inside each option.

A job check fails. An option with job (qb-target) or groups (ox_target) is hidden for anyone without that job, with no error. Test with the right job, or remove the field for a moment. On ox_target a grade in the table ({ police = 2 }) means the player needs at least that grade.

A canInteract returns nothing. If the function has a code path that returns nil, the option is hidden. Make it return true or false on every path.

The zone is in the wrong place. Turn on debug = true on ox_target (or debugPoly = true on qb-target) and look at the shape. A box centred on the floor is half underground.

The script runs before the target. Start the target resource before your script in server.cfg, and add dependency 'ox_target' to your manifest.

Checklist

Symptom Fix
Options doubled or missing Run only one of qb-target and ox_target
job ignored after converting Rename it to groups
Zone is rotated or sized wrongly heading becomes rotation; check size with debug = true
Option never shows Increase distance, test the job and canInteract
Old script stops working Convert its calls to native ox_target exports
Nothing shows at all Start ox_target and ox_lib before your scripts

Quick answers

Can I run qb-target and ox_target together?

No. Pick one. Both resources want the same targeting key and the same job, and running both gives you doubled or missing options.

Do my old qb-target scripts work with ox_target?

Often yes. ox_target provides compatibility for many qb-target exports, so scripts that call them can keep working. Test each script, and convert the ones that misbehave to the native ox_target exports.

What replaces the job field of qb-target?

ox_target uses groups. Set groups = 'police', or a table such as { police = 0 } to also require a grade.

Scripts that skip this problem

Shop CreatorBuild a shop in under a minute — owners, employees, vaults and robberies included.View script →Quest CreatorA visual editor for quests and NPC dialogues, built node by node in game.View script →Advanced BoostingTablet-driven vehicle boosting: contracts from class D to S+, crews and a live queue.View script →

Keep reading