Environment Directives
Environment directives control which runtime environments include specific code. They enable clean separation between server, client, and web code.
Available Directives
| Directive | Runtime | Description |
|---|---|---|
"use server" | Server | FiveM server runtime (Node.js) |
"use client" | Client | FiveM client runtime (game natives) |
"use web" | Web | React UI runtime (browser) |
Usage
Directives must be the first non-empty, non-comment line of the file:
"use server";
import { getMongoDb } from "@core/db/client";
export async function findUser(id: string) {
const db = await getMongoDb();
return db.collection("users").findOne({ id });
}
Build Behavior
Each directive tells the build system how to handle the file:
| Directive | Client Build | Server Build | Web Build |
|---|---|---|---|
"use client" | Keep code | Generate stubs | Generate stubs |
"use server" | Generate stubs | Keep code | Generate stubs |
"use web" | Generate stubs | Generate stubs | Keep code |
Stubs export undefined for all exports, allowing imports to resolve without including actual code.
When to Use Each Directive
"use server"
Database access, server events, player state:
"use server";
import { getMongoDb, withMongoTransaction } from "@core/db/client";
export async function createCharacter(data: CharacterData) {
const db = await getMongoDb();
return db.collection("characters").insertOne(data);
}
"use client"
FiveM natives, entity classes, HUD messaging:
"use client";
import { sendHudEvent } from "@core/hud";
export function showNotification(message: string) {
sendHudEvent("notification:show", { message });
}
"use web"
React components, Redux slices, UI features:
"use web";
import { useAppSelector } from "@ui/hooks";
export function StatusPanel() {
const health = useAppSelector((state) => state.status.health);
return <div>Health: {health}</div>;
}
Files That Need Directives
| File Type | Directive | Example |
|---|---|---|
| Repository files | "use server" | repository.ts |
| Database utilities | "use server" | @core/db/client.ts |
| Server event wrappers | "use server" | @core/events/server.ts |
| Entity classes | "use client" | @core/classes/Entity.ts |
| Client event wrappers | "use client" | @core/events/client.ts |
| HUD messaging | "use client" | @core/hud.ts |
| React components | "use web" | Panel.tsx, Page.tsx |
| Redux slices | "use web" | state/slice.ts |
| UI feature definitions | "use web" | feature.tsx |
Files That Must NOT Have Directives
The following files must NEVER have environment directives:
| File Type | Reason |
|---|---|
module.ts | Imported by all runtimes for feature discovery |
types.ts | Type-only files are runtime-agnostic |
config.ts | Usually shared configuration |
Service classes (*.service.ts) | Use decorators + __RUNTIME__ checks |
| Shared utilities | Runtime-agnostic code |
Why module.ts Must Never Have Directives
Module files are imported by:
src/runtime/client/main.ts- for client bootstrapsrc/runtime/server/main.ts- for server bootstrapui/src/modules/index.ts- for UI feature discovery
If a module.ts has "use server", the web build will stub it to export default undefined, breaking the UI's feature registry and causing:
Cannot read properties of undefined (reading 'pages')
Using __RUNTIME__ Checks
For runtime-specific code in shared files, use the global __RUNTIME__ variable:
// module.ts - NO DIRECTIVE
import { registerModule } from "@core/module";
import { fivemEvents } from "@core/events/server"; // Will be undefined in non-server
export default registerModule({
name: "my-module",
onStart() {
if (__RUNTIME__ === "server") {
// fivemEvents is available here
fivemEvents.playerJoining.on((id) => {
console.log(`Player ${id} joining`);
});
}
if (__RUNTIME__ === "client") {
// Client-only code
}
if (__RUNTIME__ === "web") {
// Web-only code (rare in modules)
}
},
});
Importing Across Environments
You can safely import from files with directives - they'll be stubbed in incompatible builds:
// This works in server build, returns undefined in client/web
import { getMongoDb } from "@core/db/client"; // "use server" file
if (__RUNTIME__ === "server") {
const db = await getMongoDb(); // Safe - we're on server
}
Best Practices
- Add directives to environment-specific files - Be explicit
- Never add directives to shared files - Use
__RUNTIME__instead - Keep module.ts directive-free - Required for feature discovery
- Use path aliases - Consistent imports regardless of runtime
- Check runtime before using stubbed imports - Avoid undefined errors