Three things to do per pet: stream the model, register it in Config.Pets, and add an inventory item. Roughly five minutes a pet.
1. Stream the model
Drop your custom prop into a stream resource. Most shoulder pets are distributed as a single .ydr (or .ydr + .ytd for textures), e.g.:
my_custom_pets/
fxmanifest.lua
stream/
cool_dragon.ydr
cool_dragon.ytd
fxmanifest.lua:
fx_version 'cerulean'
games { 'gta5' }
files { 'stream/**/*' }
data_file 'DLC_ITYP_REQUEST' 'stream/*.ytyp' -- only if you have a ytyp
Pick a model name with no spaces, lowercase, and that won’t collide with GTA’s built-in models. The model name in Config.Pets[*].model must match the .ydr filename (without the extension).
2. Register it in config.lua
Open config.lua and copy any existing entry under Config.Pets. The key on the left is the inventory item name — they must match exactly.
['cool_dragon'] = {
label = 'Cool Dragon',
model = 'cool_dragon',
attach = {
-- pos = offset from the shoulder bone in metres (x, y, z).
-- rot = rotation around that bone (pitch, roll, yaw) in degrees.
left = { pos = vec3( 0.300, 0.0, 0.15), rot = vec3(18.0, 76.0, 184.0) },
right = { pos = vec3( 0.300, 0.0, -0.15), rot = vec3(18.0, 100.0, 174.0) },
},
particle = { -- optional, remove the whole block if you don't want one
dict = 'core',
name = 'ent_dst_gen_gobstop',
pos = vec3(0.0, 0.0, -0.05),
rot = vec3(0.0, 0.0, 0.0),
scale = 0.5,
},
},
Tuning the position
- Start by copying the offsets from a similar-sized pet in the file — most of the bundled ones use
vec3(0.30, 0.0, ±0.15) and only differ in the X axis.
- Positive X pushes the pet outward away from the neck. Big or wide models usually want
0.30 – 0.35, small ones 0.27 – 0.29.
- The right shoulder uses negative Z and a yaw of
~174 so the model faces forward when mirrored.
- Adjust live by re-summoning: change the value, save the file, run
refresh then ensure nex_shoulderpets in the server console, and re-use the item.
3. Add the inventory item
Use the same key as the Config.Pets entry. For ox_inventory, add this to ox_inventory/data/items.lua:
['cool_dragon'] = {
label = 'Cool Dragon',
weight = 100,
stack = false,
close = true,
description = 'A custom dragon companion.',
},
Drop a cool_dragon.png (100×100 recommended) into ox_inventory/web/images/. Give the player the item, and you’re done.
For QBCore / Qbox, add the same item to qb-core/shared/items.lua using the standard schema. The server bridge picks up UseItem automatically. For legacy ESX inventory, add a row to the items table.
On ox_inventory, do not set consume = 0 on pet items. In Lua, 0 is truthy, so ox_inventory takes its consume branch and never calls the framework’s UseItem hook — the pet would never spawn. Leave consume unset.
4. (Optional) Particle effect
The particle block is just a StartParticleFxLoopedOnEntity call. Any vanilla particle dict / name pair works. A few good vanilla picks that exist in base GTA V (no DLC needed):
| Vibe | dict | name |
|---|
| Sparks / forge | core | sp_foundry_sparks |
| Flames | scr_bike_adversary | scr_adversary_foot_flames |
| Soap / bubbles | scr_carwash | ent_amb_car_wash_jet_soap |
| Bubbles (EMP-ish) | scr_xs_dr | scr_xs_dr_emp |
| Confetti / chaff | scr_sm_counter | scr_sm_counter_chaff |
| Leaves / dust | scr_armenian3 | ent_anim_leaf_blower |
Tune scale between 0.05 (tiny shimmer) and 0.75 (full burst). Particles play on the Trigger effect menu action and respect Config.General.cooldowns.effect.
5. Test
In the server console:
refresh
restart nex_shoulderpets
Give yourself the item:
- ox_inventory:
/giveitem <id> cool_dragon 1
- QBCore / Qbox:
/giveitem <id> cool_dragon 1
- Standalone:
/spawnpet cool_dragon
Use the item. The menu should open and your pet should attach to the configured shoulder.