Building & Installing Mods
This guide covers the full build pipeline: compiling your mod, understanding the output structure, installing it locally, and using development workflows.
Build Command
npm run buildThis compiles your mod into a ready-to-use package inside the dist/ folder. The build automatically:
- Bundles your TypeScript into a single
mod.js - Copies
manifest.jsonand assets intodist/ - Detects and includes any referenced images, audio, fonts, and other files
Customizing the Build
Your esbuild.config.ts can optionally override defaults:
import { buildMod } from "@hotbunny/hackhub-content-sdk/build";
buildMod({
entryPoint: "src/index.ts", // default
outfile: "dist/mod.js", // default
});Most projects don't need to change anything here.
Output Structure
After a successful build, dist/ contains:
dist/
├── mod.js # Bundled mod code
├── manifest.json # Copied from project root
├── cover.png # Cover image (if present in root)
├── icon.png # Icon (if present in root)
├── settings.html # HTML files from src/ (if any)
└── (other assets) # Images, audio, CSS from public/ and src/Asset Pipeline
Assets are collected from three sources:
1. public/ Directory
Everything in public/ is copied directly into dist/. Use this for static assets that aren't referenced in code:
public/
├── images/
│ └── logo.png
├── sounds/
│ └── alert.mp3
└── styles/
└── app.css2. Source-referenced Assets
Assets referenced in .ts/.tsx files with relative paths are automatically detected and copied:
// These asset references are auto-detected during build
const icon = "./assets/icon.png";
const sound = "./sfx/click.mp3";3. Root-level Assets
Files in the project root matching asset extensions (.png, .jpg, .mp3, .css, etc.) are copied to dist/. This is primarily for cover.png and icon.png.
mod-asset:// Protocol
At runtime, mods reference their assets using the mod-asset:// protocol:
mod-asset://{mod-id}/images/logo.pngIn WebView-based content (Apps, Websites), relative paths are resolved automatically:
<!-- Inside an App WebView, this resolves to mod-asset://my-mod/images/bg.png -->
<img src="images/bg.png" />In script code, use the SDK's asset utilities or construct the URL manually:
const coverUrl = `mod-asset://${manifest.id}/cover.png`;Watch Mode (Development)
For rapid iteration, use watch mode:
npm run devWhen you save a file, the mod is automatically rebuilt. To see the rebuilt mod in game, either type reloadmods in the developer console (F1) or turn on mod dev mode below.
Mod Dev Mode (auto reload)
Start the game with the --mod-dev launch option (Steam → Right-click HackHub → Properties → Launch Options). In this mode the game watches the local mods/ folder and, whenever a mod's build output changes, it saves, reloads your mods and drops you back into the save you were playing. You do not go through the main menu.
The easiest setup is to keep the whole mod project inside the game's mods folder, so npm run dev writes straight to where the game reads from:
HackHub/mods/my-awesome-mod/
├── src/
├── manifest.json
├── package.json
└── dist/ # written by npm run dev, watched by the game
├── mod.js
└── manifest.jsonOnly dist/, a root mod.js and a root manifest.json trigger a reload, so editing files in src/ does not reload the game until the build has finished. Workshop items are never watched, and mods do not run in multiplayer, so there is nothing to reload there.
reloadmods works without the launch option too. It does the same save, reload and resume in one step.
Finding Where an Error Came From
Errors thrown by mod code point at the mod and at the line in its mod.js, for example:
Cannot read properties of undefined (reading 'length') (my-awesome-mod/mod.js:120:17)You see this in the Mods menu when a mod fails to load, in the terminal when a mod command throws, and on the crash screen when a mod's UI crashes. In mod dev mode, uncaught errors from your mod also pop up in game. Open dist/mod.js at that line to find the failing code.
Installing Your Mod Locally
To test your mod in-game, copy the dist/ contents to the game's mods/ folder:
Step by Step
Build your mod:
npm run buildLocate your game's
mods/directory:- Steam:
<Steam Library>/steamapps/common/HackHub/mods/ - Or find it via Steam → Right-click HackHub → Manage → Browse local files →
mods/
Platform paths
- Windows:
mods/is next toHackhub.exe(e.g.C:\...\HackHub\mods\) - macOS:
mods/is next toHackhub.app, not inside the app bundle (e.g..../HackHub/mods/) - Linux:
mods/is next to the AppImage or executable
- Steam:
Create a folder named after your mod's
id:mods/{mod-id}/Copy everything from
dist/into that folder
HackHub/
├── Hackhub.exe (Windows) or Hackhub.app (macOS)
└── mods/
└── my-awesome-mod/ # Must match manifest.json "id"
├── mod.js
├── manifest.json
├── cover.png
└── ...- Launch (or restart) the game, or run
reloadmodsin the developer console - Open the Mods menu - your mod should appear in the list
Local Mod Priority
If a local mod and a Workshop mod share the same id, the local mod takes priority. The Workshop version is ignored as long as the local copy exists. This lets you:
- Test changes before publishing to Workshop
- Override Workshop mods with custom versions
- Develop against a published mod without affecting subscribers
Troubleshooting
Common Build Errors
| Error | Cause | Fix |
|---|---|---|
Cannot resolve "@hotbunny/hackhub-content-sdk" | SDK not installed | Run npm install |
manifest.json not found | Missing manifest | Create manifest.json in project root |
experimentalDecorators warning | Incorrect tsconfig | Build auto-fixes this; re-run build |
Could not resolve "./foo.html" | Missing HTML file | Ensure the file exists at the referenced path |
Mod Not Appearing In-Game
- Verify the folder name in
mods/matches theidfield inmanifest.json - Check that
manifest.jsonexists inside the mod folder (not nested deeper) - Ensure
mod.js(the entry field) is present - Check the game's developer console for error messages
Assets Not Loading
- Confirm assets are in
dist/after building - For WebView content (Apps, Websites), use relative paths - they resolve automatically
- For script code, use the full
mod-asset://{id}/pathURL - Ensure asset file names match exactly (case-sensitive)
