PiTyUs.Hire me

Engineering

Writing FiveM resources in TypeScript

Build FiveM resources with TypeScript: the @citizenfx/client and @citizenfx/server typings, bundling with esbuild into client and server files, the fxmanifest, Node.js 16 and optional Node 22 on the server, events, exports typing with CitizenExports, and when TypeScript is worth it.

Updated 11 min readBy PiTyUs · FiveM developer

Overview

FiveM runs JavaScript natively on both client and server, and TypeScript compiles to it. For larger resources — or teams used to web development — TypeScript’s types, imports and npm ecosystem are a real advantage over plain Lua. The setup is a small build step.

01Project setup

Terminalbash
npm init -y
npm install --save-dev typescript esbuild @citizenfx/client @citizenfx/server
Layouttext
my_resource/
  fxmanifest.lua
  package.json
  build.mjs
  src/client/main.ts
  src/server/main.ts
  dist/            -- generated

02Bundling

build.mjsjs
import { build } from 'esbuild';

await build({ entryPoints: ['src/client/main.ts'], bundle: true, outfile: 'dist/client.js', platform: 'browser', target: 'es2017' });
await build({ entryPoints: ['src/server/main.ts'], bundle: true, outfile: 'dist/server.js', platform: 'node', target: 'node16' });
fxmanifest.lualua
fx_version 'cerulean'
game 'gta5'

client_script 'dist/client.js'
server_script 'dist/server.js'
-- node_version '22'  -- opt in to Node.js 22 for server scripts

The client target matches the ES2017 standard library FiveM’s client runtime provides. If you opt in to Node 22, raise the server target accordingly.

Writing code

src/server/main.tsts
onNet('fishmarket:sell', () => {
  const src = global.source;
  const ped = GetPlayerPed(String(src));
  const [x, y, z] = GetEntityCoords(ped);
  console.log(`player ${src} at ${x.toFixed(1)}, ${y.toFixed(1)}, ${z.toFixed(1)}`);
});

exports('getPrice', (item: string) => (item === 'fish' ? 45 : 0));
src/client/main.tsts
RegisterCommand('sell', () => {
  emitNet('fishmarket:sell');
}, false);

Events: on/onNet to listen, emit/emitNet to trigger. Server natives take player IDs as strings in the typings. Security rules are the same as in Lua — see secure server events.

Typing exports

src/types.d.tsts
declare global {
  interface CitizenExports {
    fishmarket: {
      getPrice(item: string): number;
    };
  }
}
export {};

When TypeScript is worth it

  • Large resources with shared data types between client, server and NUI.
  • Teams that already write TypeScript for the web.
  • Server code that benefits from npm packages (HTTP clients, validation).
  • Frameworks with JS APIs, such as ox_core’s npm package — see ox_core overview.

For small scripts, Lua remains quicker to write and is what most tutorials use — see choosing a language.

Frequently asked questions

Can I write FiveM scripts in TypeScript?

Yes. Compile or bundle TypeScript to JavaScript and list the output in the fxmanifest.

Which Node.js version does FiveM use?

Node.js 16 on the server by default; add node_version '22' to the manifest for Node 22.

Can I use npm packages in FiveM?

On the server, yes (bundled or from node_modules). On the client, only pure JS without browser or Node APIs.

Where do FiveM TypeScript typings come from?

The official @citizenfx/client and @citizenfx/server npm packages.

Need this built, not just explained?

Ten years of FiveM work, from Lua to NUI

Custom resources, React NUI, ESX / QBCore / Qbox integration, OneSync performance audits and security reviews — plus the websites and SEO around your server brand.

Related guides