FiveM & QBCore · Practical guide

QBCore Inventory Guide

A practical, security-first guide to designing and implementing a custom inventory workflow for QBCore-based FiveM servers. Covers item definitions, server-authority patterns,.

Stellar AI · Updated 8 September 2026 · 4 min read

This guide teaches builders how to design and implement a robust inventory system for QBCore servers. It focuses on server-authoritative patterns, item lifecycle (add, remove, use), weight/stack validation, and safe client/server communication. Example files are included for a small resource that adds a consumable item and a secure stash interaction.

Why server authority matters

Inventory data is a core trust boundary. Always treat the server as the source of truth: only the server should add/remove items, check weight and permissions, or change player money. Clients should request actions and render UI only. This prevents exploits and keeps synchronisation reliable across reconnects.

Design checklist (quick)

AreaAction
Item definitionsRegister in shared config and server-side item list; include weight and stack size
Adding/removingOnly on server via QBCore Player.Functions.AddItem/RemoveItem
Usable itemsUse QBCore.Functions.CreateUseableItem on server to handle use
Client actionsTrigger server events; never mutate inventory locally
ValidationServer checks weight, inventory space, ownership, and permissions

Core components

A minimal QBCore inventory interaction typically has:

  • config.lua / shared definitions (weights, limits)
  • server.lua (item handlers, CreateUseableItem, callbacks)
  • client.lua (UI triggers, animations, targets)
  • fxmanifest.lua (resource manifest and dependencies)

Item model

Keep item definitions small and canonical. Example fields: name, label, weight, stack. Store them in one shared file so UI and server logic use the same values.

Complete example resource files

The example below creates a consumable "energy_drink" and a secure stash interaction that validates ownership on the server. Place these files in a resource folder, e.g. qb-custom-inventory.

fxmanifest.lua

fx_version 'cerulean'
game 'gta5'

name 'qb-custom-inventory'
author 'YourName'
description 'Small example: consumable item and server-validated stash'
version '1.0.0'

shared_script 'config.lua'

server_script 'server.lua'
client_script 'client.lua'

dependencies {
  'qb-core'
}

config.lua

Config = {}

Config.Items = {
  energy_drink = {
    label = 'Energy Drink',
    weight = 200, -- grams
    stack = 5,
  }
}

-- Stash config: example key is stash id stored in DB or on server
Config.Stashes = {
  ['house_stash_1'] = {
    label = 'Safe Stash',
    ownerOnly = true
  }
}

server.lua

local QBCore = exports['qb-core']:GetCoreObject()

-- Register usable item
QBCore.Functions.CreateUseableItem('energy_drink', function(source, item)
  local Player = QBCore.Functions.GetPlayer(source)
  if not Player then return end

  -- Server-side validation: ensure player still has the item
  local hasItem = Player.Functions.GetItemByName('energy_drink')
  if not hasItem then
    TriggerClientEvent('QBCore:Notify', source, 'You do not have an energy drink', 'error')
    return
  end

  -- Remove the item on server and trigger a client effect
  local removed = Player.Functions.RemoveItem('energy_drink', 1, item.slot)
  if removed then
    TriggerClientEvent('qb-custom-inventory:client:consumeEnergy', source)
  else
    TriggerClientEvent('QBCore:Notify', source, 'Could not remove item', 'error')
  end
end)

-- Secure stash open: server checks ownership before giving inventory access
QBCore.Functions.CreateCallback('qb-custom-inventory:server:canOpenStash', function(source, cb, stashId)
  local Player = QBCore.Functions.GetPlayer(source)
  if not Player then cb(false) return end

  local stash = Config.Stashes[stashId]
  if not stash then cb(false) return end

  if stash.ownerOnly then
    -- Example check: assume player metadata stores house id
    local ownerHouse = Player.PlayerData.metadata and Player.PlayerData.metadata.houseId
    if ownerHouse == stashId then
      cb(true)
    else
      cb(false)
    end
  else
    cb(true)
  end
end)

client.lua

local QBCore = exports['qb-core']:GetCoreObject()

RegisterNetEvent('qb-custom-inventory:client:consumeEnergy')
AddEventHandler('qb-custom-inventory:client:consumeEnergy', function()
  -- Client-side effect only: animation, heal, sound
  -- All state changes were validated and performed server-side
  QBCore.Functions.Notify('You feel energized', 'success')
  -- Example: trigger a small health restore client-side
  local ped = PlayerPedId()
  local health = GetEntityHealth(ped)
  SetEntityHealth(ped, math.min(200, health + 20))
end)

-- Example: request stash open via qb-target or key
function TryOpenStash(stashId)
  QBCore.Functions.TriggerCallback('qb-custom-inventory:server:canOpenStash', function(canOpen)
    if canOpen then
      -- Open qb-inventory or custom UI; server remains authoritative
      TriggerEvent('inventory:client:OpenStash', stashId)
    else
      QBCore.Functions.Notify('You are not allowed to open this stash', 'error')
    end
  end, stashId)
end

Security and validation notes

  • Never trust client-sent item amounts or slots. Always call Player.Functions.GetItemByName and Player.Functions.RemoveItem/AddItem on the server and validate return values.
  • Use callbacks for small queries (existence, ownership) and server events for state changes. Callbacks return values synchronously to the client script with server checks in place.
  • Limit exposed events; name them with your resource prefix (e.g. qb-custom-inventory:server:removeItem) to avoid collisions.
  • Check for race conditions: if you allow rapid item use, consider server-side locks or re-checks after database writes.

Integration tips

If you use qb-inventory or ox_inventory frontends, keep item metadata compatible and translate only on the server-side. For target interactions prefer qb-target or ox_target to keep client code small: they trigger the TryOpenStash function above, which performs server-validated checks.

Stellar AI can help you iterate on resource structure and file creation. If you want to prototype multiple item types and UI screens quickly, try the builder at https://trystellarai.com/app. When you have a small working demo, use the same workspace to expand interactions and automate file scaffolding via templates available in the app: https://trystellarai.com/app. For reading and deeper background posts about scripting patterns, see the Stellar AI blog: https://trystellarai.com/blog.

Validation steps and next steps

  1. Install resource folder and ensure fxmanifest.lua references qb-core in your server cfg.
  2. Start the server and test: give a test player the item via server console or qb-core admin commands.
  3. Attempt to use the item and verify server removes it and client shows effects.
  4. Attempt client-only mutations (e.g. fake calls to client events) to confirm server-only changes prevent exploits.
  5. Expand: add weight checks on AddItem and refuse adds if capacity exceeded.

Next steps: add logging for inventory changes, hook DataStore backups for persistent stash contents, and add unit tests for server callbacks if you have a CI environment. Keep UIs thin and let the server own all business logic.

Build your next system with Stellar AI

Describe one feature, get organized project files, then bring back your errors to keep improving. Start free with no card required. Test generated code in a private development environment before release.

Create your first script free →