The Book·Chapter 10/12·8 min

VSCode ready for FiveM

Get VSCode ready to code FiveM resources: Lua and natives extensions, a .luarc.json that recognizes the globals, your own snippets, Git and a fast iteration loop.

So far we've talked about resources, fxmanifest and Lua. But writing that code in Notepad is painful: no colors, no autocomplete, no error warnings. A good editor saves you hours and teaches you as you type. In FiveM the de facto standard is Visual Studio Code (VSCode): free, lightweight, cross-platform and with the best Lua support you're going to find. In this chapter we get it tuned for FiveM from start to finish.

Installing VSCode

Download it from code.visualstudio.com, install it with the default options and open it. The key trick to working comfortably: open the FOLDER of your resource (or the entire resources/ folder), not loose files. In VSCode you do this with "File → Open Folder". When you open a folder, the editor understands the project as a whole and the extensions can analyze all your scripts together.

The extensions that matter

VSCode ships empty on purpose; you give it superpowers with extensions from the Extensions tab (Ctrl+Shift+X). These are the ones that truly move the needle for FiveM:

  • Lua (by sumneko / LuaLS): the most important one. It's the Lua Language Server: autocomplete, real-time error diagnostics, go to definition and documentation on hover. Without it you're flying blind.
  • A FiveM natives extension (search for "FiveM" or "cfx natives"): adds autocomplete and descriptions for the Citizen/Cfx natives (CreateThread, GetEntityCoords, TriggerEvent…), which number in the thousands.
  • vscode-fivem (or similar): highlights fxmanifest.lua and provides FiveM-specific snippets and conveniences.
  • GitLens: supercharges the Git that VSCode already includes (see who changed each line, history, compare versions). Version control is built in out of the box.
  • EditorConfig: enforces a shared indentation and line-ending style across the whole team, no arguments.
  • Prettier (optional): formats the JavaScript/HTML/CSS of your NUI interfaces. You don't need it for Lua; LuaLS already handles that.

The .luarc.json: making it stop flagging false errors

Right after installing LuaLS you'll run into red underlines everywhere: "Undefined global Citizen", "Undefined global exports", "Undefined global ESX"… It's not a mistake on your part. LuaLS doesn't know you're in FiveM and is unaware of the global variables the engine injects for you. The fix is to create a .luarc.json file at the root of the resource to declare those globals and where your framework's libraries live.

json
{
  "$schema": "https://raw.githubusercontent.com/LuaLS/vscode-lua/master/setting/schema.json",
  "runtime.version": "Lua 5.4",
  "diagnostics.globals": [
    "Citizen",
    "exports",
    "GetCurrentResourceName",
    "source",
    "vector2",
    "vector3",
    "vector4",
    "quat",
    "ESX",
    "QBCore",
    "TriggerEvent",
    "TriggerServerEvent",
    "TriggerClientEvent",
    "RegisterNetEvent",
    "AddEventHandler"
  ],
  "workspace.library": [
    "C:/FXServer/server-data/resources/[core]/es_extended"
  ],
  "workspace.checkThirdParty": false
}

.luarc.json (resource root)

  • diagnostics.globals: list of names LuaLS should treat as valid globals (the ones FiveM and your framework inject). This stops the "undefined" warnings.
  • workspace.library: paths to external code you want LuaLS to read for autocomplete. Pointing it at es_extended (ESX) or qb-core (QBCore) gives you suggestions for their functions.
  • runtime.version: FiveM uses Lua 5.4; declare it so the analysis lines up.

Adjust workspace.library to YOUR real server path. If you work with several resources, you can put a .luarc.json at the root of resources/ and have all of them inherit it.

Snippets: type less, repeat less

There are blocks you type a thousand times: registering an event, a command, a thread. Snippets let you type a shortcut (for example rnet) and have VSCode complete the whole template, with tab stops to jump between the gaps. To create your own: "File → Preferences → Configure Snippets", choose Lua and add your template. Here are three very handy ones:

lua
-- rnet -> register and handle a network event
RegisterNetEvent('mi_recurso:miEvento')
AddEventHandler('mi_recurso:miEvento', function(arg)
  -- your logic here
end)

-- rcmd -> register a chat command
RegisterCommand('micomando', function(source, args, raw)
  -- your logic here
end, false)

-- thread -> loop in its own thread
CreateThread(function()
  while true do
    Wait(1000)
    -- your logic here
  end
end)

Patterns worth keeping as snippets

Git: version your server from day one

Versioning with Git isn't just "for big teams". It's your safety net: if a change breaks the server, you roll back to the previous state in seconds; if you want to try something risky, you do it on a branch; and you keep a history of what changed and why. VSCode ships with Git built in (the source control tab). The one critical thing is to never commit secrets or junk. That's what the .gitignore at the root of the repository is for:

text
# Secrets and sensitive config. NEVER to the repository
secrets.cfg
.env
*.key

# Cache and server-generated data
cache/
logs/
*.log

# NUI dependencies (reinstalled with npm install)
node_modules/

# System and editor junk
.DS_Store
Thumbs.db

Minimal .gitignore

Your Cfx license key, Discord tokens or database credentials go in secrets.cfg (loaded with exec) or in environment variables, NEVER in versioned files. If a key ends up in a public repository, consider it compromised and regenerate it.

The day-to-day workflow

With the editor tuned, your iteration cycle is fast and always the same: you edit the code in VSCode, reload it on the server and read the console to see what happened. You don't need to restart the whole server for every change: just restart the affected resource.

  • Edit and save the file in VSCode (Ctrl+S).
  • In the server console (or txAdmin), run restart mi_recurso. If you added new files to the manifest, run ensure mi_recurso or refresh first.
  • Read the console: that's where your print()s, Lua errors and the stack trace show up if something blows up. The console is your best friend.
  • Repeat. Small change, reload, observe. That's how you isolate which line causes each effect.

For debugging, well-placed print()s are still the most honest tool: print the value of a variable right before the suspicious line and check whether it holds what you think. A well-placed print('[DEBUG] money =', money) solves most mysteries.

Productivity tip: with LuaLS installed and a well-configured .luarc.json, you catch half the bugs BEFORE starting the server. The editor flags the misspelled variable, the missing argument or the nil you're about to cause as you type, not two hours later reading the console.

Put it into practice

Got a question about this? The AI chat knows all of it and answers with code.

Ask the AI
Setting up VSCode for FiveM development | Crxative-M