This resource exposes a powerful hooks system that lets other resources influence, manipulate, or react to the crafting flow. Hooks can be used to veto operations, modify data, or trigger side effects.
These hooks allow you to modify the data that gets used in the crafting system. Return a table with the properties you want to change, and they will be merged into the context.
crafting:canSeeBench - Runs when a player enters proximity of a bench (before they can see it)
Can veto: return false or {success = false, message = 'key'} to hide the bench from the player
This hook runs in the proximity system, so if it returns false, the player will never see the bench
crafting:canInteract - Runs when checking if a player can interact with a bench
Can manipulate: benchType
Can veto: return false or {success = false, message = 'key'}
crafting:enter - Runs when a player enters a crafting bench
Can manipulate: benchType
Can veto: return false or {success = false, message = 'key'}
crafting:exit - Runs when a player exits a crafting bench
Can manipulate: benchType
Can veto: return false or {success = false, message = 'key'}
crafting:getCategories - Runs when fetching categories for a player
Can manipulate: categories (array of category objects)
Can veto: return false or {success = false, message = 'key'}
crafting:getCategoryRecipes - Runs when fetching recipes for a category
Can manipulate: recipes (array of recipe objects), totalRecipes, hasMore, nextPage
Can veto: return false or {success = false, message = 'key'}
crafting:getRecipe - Runs when fetching a single recipe for a player
Can manipulate: recipe (all properties like craftingTime, ingredients, results, etc.)
Can veto: return false or {success = false, message = 'key'}
crafting:preCraft - Runs right before a craft is accepted
Can manipulate: recipe (all properties like craftingTime, ingredients, results, etc.), quantity
Can veto: return false or {success = false, message = 'key'}
In your resource (fxmanifest): export a function, e.g. myPreCraftCheck.
-- In your fxmanifest.luaexports { 'myPreCraftCheck'}-- In your server filefunction myPreCraftCheck(ctx) -- Your logic here return trueend-- Register hook by export nameexports['nextgenfivem_crafting']:registerHook('crafting:preCraft', { priority = 10, resource = GetCurrentResourceName(), export = 'myPreCraftCheck'})
true or nil → allow and continue to the next handler (no changes)
false → stop operation, use default message crafting.not_authorized
{ success = false, message = 'errors.key' } → stop operation with the given translation key
false, 'errors.key' → stop operation with the given translation key
Table with modifications → merge changes into context and continue
When returning a table for manipulation, you can modify nested properties. The system uses deep merging, so you only need to specify the properties you want to change:
exports['nextgenfivem_crafting']:registerHook('crafting:getCategories', { priority = 0, fn = function(ctx) local playerRank = exports['my_framework']:getPlayerRank(ctx.src) -- Hide premium categories for non-premium players if playerRank < 5 then local filteredCategories = {} for _, category in ipairs(ctx.categories) do if not category.isPremium then table.insert(filteredCategories, category) end end return { categories = filteredCategories } end return true end})
exports['nextgenfivem_crafting']:registerHook('crafting:getCategoryRecipes', { priority = 0, fn = function(ctx) local playerLevel = exports['my_leveling']:getPlayerLevel(ctx.playerId) -- Modify each recipe's crafting time based on player level local modifiedRecipes = {} for _, recipe in ipairs(ctx.recipes) do local reduction = math.min(0.3, playerLevel * 0.02) -- 2% per level, max 30% local newRecipe = table.clone(recipe, true) newRecipe.craftingTime = math.max(1, recipe.craftingTime * (1 - reduction)) table.insert(modifiedRecipes, newRecipe) end return { recipes = modifiedRecipes } end})
exports['nextgenfivem_crafting']:registerHook('crafting:canSeeBench', { priority = 0, fn = function(ctx) local playerBanned = exports['my_system']:isPlayerBanned(ctx.playerId) -- Hide bench from banned players if playerBanned then return false end local playerLevel = exports['my_leveling']:getPlayerLevel(ctx.playerId) -- Hide premium benches from low-level players if ctx.benchType.isPremium and playerLevel < 10 then return false end return true end})
exports['nextgenfivem_crafting']:registerHook('crafting:canInteract', { priority = 0, fn = function(ctx) local playerVIP = exports['my_vip']:isVIP(ctx.playerId) if playerVIP then -- VIP players get access to full bench even if it's restricted return { benchType = { fullAccess = true } } end return true end})
local cooldown = {}exports['nextgenfivem_crafting']:registerHook('crafting:preCraft', { priority = 10, fn = function(ctx) local key = ctx.playerId .. ':' .. ctx.recipe.uuid local now = os.time() if cooldown[key] and cooldown[key] > now then return false, 'errors.cooldown' end cooldown[key] = now + 30 -- 30 seconds return true end})
-- This example shows how to completely customize a bench for a specific playerexports['nextgenfivem_crafting']:registerHook('crafting:enter', { priority = 0, fn = function(ctx) local playerData = exports['my_system']:getPlayerData(ctx.playerId) -- Customize the entire bench for this player if playerData.hasSpecialAccess then return { benchType = { fullAccess = true, -- Add any other bench properties you want to modify } } end return true end})