Skip to main content

Creating Modules in RPGJS

This guide explains the canonical RPGJS module pattern. defineModule() authors runtime behavior and provideServerModules() or provideClientModules() installs it.

Module Structure

A module that runs on both sides consists of two explicit files:
  • server.ts - Server-side logic and hooks
  • client.ts - Client-side logic and hooks
Code used by both runtimes can live in a separate shared.ts file.

Bundle ownership

The entry point that imports a module determines which bundle contains it. The file name makes ownership clear, but it does not override an explicit import. In MMORPG mode, install server.ts only from the server bootstrap and client.ts only from the client configuration. The browser build then excludes the server module and its imports.
Standalone mode runs the RPGJS server in the browser. Its bundle therefore contains both client and server modules. Never place credentials, private keys, service tokens, or other secrets in a game compiled for standalone mode.
Follow these boundaries when structuring a module:
  • do not import server.ts from client.ts
  • do not import an index.ts that eagerly imports both runtimes from browser code
  • keep shared.ts limited to types, serializable data, constants, and logic safe to expose to a browser
  • keep authoritative validation for movement, combat, inventory, and saves on the server, even when the implementation is absent from the client bundle
Reusable packages should expose separate /client and /server entry points instead of asking applications to import a combined runtime entry.

Step 1: Define Server-Side Module

Create a server.ts file using defineModule to define server-side behavior:

Step 2: Define Client-Side Module

Create a client.ts file using defineModule for client-side logic:

Step 3: Install Each Runtime Module

Install the server definition in server.ts:
Install the client definition in the shared client configuration:
Standalone and MMORPG clients use this same client configuration. In MMORPG mode, the server definition is built separately and remains authoritative for gameplay state. In standalone mode, that definition executes in the browser. Reusable packages should expose a provideFeature(options) function from explicit /server and /client entry points. Direct DI composition with createModule() remains available for advanced integrations, but is not needed for ordinary gameplay modules.