SPTBridgeUI.Core
1.0.0
See the version list below for details.
dotnet add package SPTBridgeUI.Core --version 1.0.0
NuGet\Install-Package SPTBridgeUI.Core -Version 1.0.0
<PackageReference Include="SPTBridgeUI.Core" Version="1.0.0" />
<PackageVersion Include="SPTBridgeUI.Core" Version="1.0.0" />
<PackageReference Include="SPTBridgeUI.Core" />
paket add SPTBridgeUI.Core --version 1.0.0
#r "nuget: SPTBridgeUI.Core, 1.0.0"
#:package SPTBridgeUI.Core@1.0.0
#addin nuget:?package=SPTBridgeUI.Core&version=1.0.0
#tool nuget:?package=SPTBridgeUI.Core&version=1.0.0
BridgeUI
A framework for SPT Tarkov mod developers to create web-based UIs using any frontend framework (React, Vue, Svelte, plain HTML/JS) while maintaining type safety with their C# backend.
Features
- 🎨 Any Frontend - Use React, Vue, Svelte, or vanilla HTML/JS
- 🔒 End-to-End Type Safety - Auto-generate TypeScript types and API clients from C#
- 🔥 Hot Reload - Dev server proxy for instant updates during development
- 🔌 Simple API Definition - Attribute-based API endpoints with automatic routing
- 📁 Static File Serving - Built-in serving of bundled frontend with SPA support
- ⚡ Minimal Boilerplate - Extend
WebUiModBaseand start building
Quick Start
1. Add the SDK to Your Project
Option A: Install as SPT Mod Dependency (Recommended)
Install the BridgeUI mod into your SPT installation. Then add it as a dependency in your mod's metadata:
public override Dictionary<string, SemanticVersioning.Range>? ModDependencies { get; init; } = new()
{
{ "com.spt.bridgeui", new("~1.0.0") }
};
This ensures the SDK is loaded before your mod and shared across all Web UI mods.
Option B: Direct Reference
Include the SDK DLL directly in your mod folder and reference it in your .csproj:
<ItemGroup>
<Reference Include="SPT.BridgeUI.Core">
<HintPath>path/to/SPT.BridgeUI.Core.dll</HintPath>
</Reference>
</ItemGroup>
2. Create Your Web UI Class
using System.Reflection;
using SPT.BridgeUI.Core;
using SPT.BridgeUI.Core.Attributes;
using SPTarkov.DI.Annotations;
using SPTarkov.Server.Core.Helpers;
using SPTarkov.Server.Core.Models.Utils;
[Injectable(TypePriority = 0)] // Required for SPT to discover as IHttpListener
public class MyModWebUi : WebUiModBase
{
// URL path for your mod's web UI (e.g., https://127.0.0.1:6969/mymod/)
protected override string BasePath => "/mymod";
public MyModWebUi(ModHelper modHelper, ISptLogger<MyModWebUi> logger)
: base(modHelper.GetAbsolutePathToModFolder(Assembly.GetExecutingAssembly()))
{
//
}
// Define API endpoints with attributes - no boilerplate!
[ApiEndpoint("/mymod/api/data", "GET", Name = "getData", Description = "Yummy data")]
public MyData GetData() => _myService.GetData();
// Providing a `Name` attribute will allow you to automatically generate a type-safe function
// In this example, your frontend will be able to just call `saveData(data)` with full type-safety!
[ApiEndpoint("/mymod/api/data", "POST", Name = "saveData")]
public object SaveData(MyData data)
{
_myService.Save(data);
return new { success = true };
}
}
3. Add Your Frontend
Place your built frontend in the wwwroot folder of your mod:
YourMod/
├── YourMod.dll
├── YourMod.deps.json
├── SPT.BridgeUI.Core.dll # Only needed if using Option B (direct reference)
└── wwwroot/
├── index.html
├── styles.css
└── app.mjs # ⚠️ Use .mjs, NOT .js!
⚠️ IMPORTANT: JavaScript files must use
.mjsextension (not.js).
SPT's mod validator rejects mods containing.jsor.tsfiles, treating them as legacy TypeScript mods.
4. Access Your UI
Navigate to https://127.0.0.1:6969/mymod/
Complete Example
Here's a minimal working example:
MyModWebUi.cs:
[Injectable(TypePriority = 0)]
public class MyModWebUi : WebUiModBase
{
protected override string BasePath => "/mymod";
public MyModWebUi(ModHelper modHelper, ISptLogger<MyModWebUi> logger)
: base(modHelper.GetAbsolutePathToModFolder(Assembly.GetExecutingAssembly()))
{ }
[ApiEndpoint("/mymod/api/hello", "GET")]
public object Hello() => new { message = "Hello from my mod!" };
}
wwwroot/index.html:
<!DOCTYPE html>
<html>
<head>
<title>My Mod</title>
</head>
<body>
<h1>My Mod</h1>
<div id="message"></div>
<script type="module" src="app.mjs"></script>
</body>
</html>
wwwroot/app.mjs:
const response = await fetch("/mymod/api/hello");
const data = await response.json();
document.getElementById("message").textContent = data.message;
That's it! ~15 lines of C# + simple HTML/JS = working web UI for your SPT mod.
💡 Want type safety? This minimal example uses vanilla JS without type checking. To get end-to-end type safety, use a TypeScript frontend (React, Vue, etc.) with our
spt-bridgeui-typegenCLI to auto-generate typed API clients. See API Client Generation below.
Development with Hot Reload
For a better development experience with hot module replacement:
1. Set the Dev Server URL
Set an environment variable before starting the SPT server:
# Windows PowerShell
$env:SPT_WEBUI_DEV_URL = "http://localhost:5173"
# Windows CMD
set SPT_WEBUI_DEV_URL=http://localhost:5173
2. Configure Your Frontend Dev Server
Vite example (vite.config.ts):
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
export default defineConfig({
plugins: [react()],
base: "/mymod/", // Must match your BasePath
build: {
outDir: "../Server/wwwroot",
emptyOutDir: true,
},
server: {
port: 5173,
proxy: {
"/mymod/api": {
target: "https://127.0.0.1:6969",
secure: false, // Accept self-signed cert
changeOrigin: true,
},
},
},
});
3. Run Both Servers
# Terminal 1: Frontend dev server
cd frontend
npm run dev
# Terminal 2: SPT Server
cd <SPT_ROOT>
./SPT.Server.exe
Now changes to your frontend update instantly!
API Reference
WebUiModBase
The base class for web UI mods. Extend this and configure:
| Property | Type | Default | Description |
|---|---|---|---|
BasePath |
string |
(required) | URL prefix for this mod (e.g., /mymod) |
DistFolder |
string |
"wwwroot" |
Path to frontend files relative to mod folder |
IndexFile |
string |
"index.html" |
Index file for SPA routing |
DevServerUrl |
string? |
env var | Dev server URL for hot reload |
EnableSpaFallback |
bool |
true |
Serve index.html for unknown routes |
CacheDuration |
TimeSpan |
1 hour | Cache duration for static assets |
ApiEndpointAttribute
Mark methods as API endpoints:
[ApiEndpoint(route, method, Name = "functionName", Description = "optional")]
| Parameter | Required | Description |
|---|---|---|
route |
Yes | The full URL path (e.g., /mymod/api/config) |
method |
Yes | HTTP method (GET, POST, PUT, DELETE, PATCH) |
Name |
No | Name for the generated TypeScript function (e.g., getConfig) |
Description |
No | JSDoc comment in generated code; useful for documentation |
💡 Tip: Always set
Nameif you want to use the auto-generated API client!
Supported return types:
- Any object (serialized as JSON)
Task<T>for async operationsvoid/Task(returns{ "success": true })
Request body:
For POST/PUT/PATCH, the first parameter is deserialized from the request body:
[ApiEndpoint("/mymod/api/save", "POST")]
public object Save(MyData data)
{
// data is automatically deserialized from JSON body
return new { success = true };
}
Sample Project
See the samples/SimpleCounter directory for a complete working example with:
- C# backend with API endpoints
- State persistence to
state.json - React + TypeScript frontend (bundled with Vite)
- Auto-generated TypeScript types and API client
- Tarkov-inspired dark theme styling
See the demo for yourself:
- Install the server mod by copying the contents of
SimpleCounter/Server/distto the root of your SPT 4.0 installation - Start your server
- Go to https://127.0.0.1:6969/counter
Project Structure
SPT.WebUI.SDK/
├── src/
│ └── SPT.BridgeUI.Core/ # Core SDK library
│ ├── WebUiModBase.cs # Base class for mods
│ ├── Attributes/
│ │ └── ApiEndpointAttribute.cs
│ ├── Handlers/
│ │ ├── StaticFileHandler.cs
│ │ └── DevServerProxy.cs
│ └── Utils/
│ └── MimeTypes.cs
├── samples/
│ └── SimpleCounter/ # Example mod
│ ├── Server/ # C# backend
│ └── frontend/ # HTML/JS frontend
└── README.md
Important Notes
File Extensions
- ✅ Use
.mjsfor JavaScript modules - ❌ Do NOT use
.jsor.tsfiles (SPT rejects these)
SDK Distribution
The SPT.BridgeUI.Core.dll must be available at runtime. Options:
- Shared SDK mod (recommended) - Install
BridgeUImod and declare it as a dependency - Include in mod folder - Bundle the DLL directly with your mod
Required Attribute
Always use [Injectable(TypePriority = 0)] on your WebUiModBase class. This registers it as an IHttpListener with SPT's dependency injection system.
Type Generation
Generate TypeScript types and API clients from your C# code using the CLI tool.
Installation
# Install as a global tool
dotnet tool install --global SPTBridgeUI.TypeGen
# Or run from source during development
dotnet run --project src/SPT.BridgeUI.TypeGen -- [options]
Usage
# Simple! References are auto-discovered from NuGet cache
spt-bridgeui-typegen --assembly path/to/YourMod.dll --output frontend/src/api
Options
| Option | Alias | Description |
|---|---|---|
--assembly |
-a |
Path to your compiled mod DLL (required) |
--output |
-o |
Output directory for generated files (default: ./types) |
--types-file |
Output filename for types (default: api-types) |
|
--client-file |
Output filename for API client (default: api-client) |
|
--namespace |
-n |
Only export types from this namespace (optional) |
--refs |
-r |
Additional reference paths (usually auto-detected) |
--no-auto-refs |
Disable auto-discovery of NuGet/ASP.NET references | |
--verbose |
-v |
Show detailed output including discovered references |
--watch |
-w |
Watch for assembly changes and auto-regenerate |
Mark Types for Export
Use the [ExportTs] attribute on C# types:
using SPT.BridgeUI.Core.Attributes;
[ExportTs]
public class PlayerStats
{
public int Level { get; set; }
public string Name { get; set; }
public List<string> Skills { get; set; }
}
[ExportTs]
public enum PlayerStatus
{
Online,
Away,
Offline
}
Generates api-types.ts:
export interface PlayerStats {
level: number;
name: string;
skills: string[];
}
export enum PlayerStatus {
Online = 0,
Away = 1,
Offline = 2,
}
API Client Generation
The CLI tool automatically generates type-safe API client functions from your [ApiEndpoint] attributes. This means you define your API once in C# and get fully typed TypeScript functions for free!
Define Your Models and Endpoints
Step 1: Create request/response models with [ExportTs]:
[ExportTs]
public class CounterState
{
public int Count { get; set; }
public DateTime LastUpdated { get; set; }
}
[ExportTs]
public class AdjustCounterRequest
{
public int Amount { get; set; } = 1;
}
Step 2: Create endpoints with [ApiEndpoint] and Name:
[ApiEndpoint("/counter/api/state", "GET", Name = "getCounterState")]
public CounterState GetState() => _state;
[ApiEndpoint("/counter/api/increment", "POST", Name = "incrementCounter")]
public CounterState Increment(AdjustCounterRequest request)
{
_state.Count += request.Amount;
return _state;
}
[ApiEndpoint("/counter/api/decrement", "POST", Name = "decrementCounter")]
public CounterState Decrement(AdjustCounterRequest request)
{
_state.Count -= request.Amount;
return _state;
}
Step 3: Run the generator:
# References auto-discovered - no --refs needed!
spt-bridgeui-typegen --assembly YourMod.dll --output frontend/src/api
Generated Output
api-types.ts - Your C# models as TypeScript:
export interface CounterState {
count: number;
lastUpdated: string;
}
export interface AdjustCounterRequest {
amount: number;
}
api-client.ts - Type-safe fetch functions:
import type { CounterState, AdjustCounterRequest } from "./api-types";
export async function getCounterState(): Promise<CounterState> {
const response = await fetch("/counter/api/state");
if (!response.ok) throw new Error(`HTTP ${response.status}`);
return response.json();
}
export async function incrementCounter(
request: AdjustCounterRequest
): Promise<CounterState> {
const response = await fetch("/counter/api/increment", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(request),
});
if (!response.ok) throw new Error(`HTTP ${response.status}`);
return response.json();
}
export async function decrementCounter(
request: AdjustCounterRequest
): Promise<CounterState> {
const response = await fetch("/counter/api/decrement", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(request),
});
if (!response.ok) throw new Error(`HTTP ${response.status}`);
return response.json();
}
Use in React with Full Type Safety
import {
getCounterState,
incrementCounter,
decrementCounter,
} from "./api/api-client";
import type { CounterState } from "./api/api-types";
function Counter() {
const [state, setState] = useState<CounterState | null>(null);
const handleIncrement = async (amount: number) => {
// ✅ TypeScript knows `incrementCounter` expects { amount: number }
// ✅ TypeScript knows it returns Promise<CounterState>
const newState = await incrementCounter({ amount });
setState(newState);
};
return (
<div>
<span>{state?.count}</span>
<button onClick={() => handleIncrement(1)}>+1</button>
<button onClick={() => handleIncrement(5)}>+5</button>
<button onClick={() => handleDecrement({ amount: 1 })}>-1</button>
</div>
);
}
The magic: Change your C# model, regenerate, and TypeScript immediately catches any mismatches! 🎯
Reference Paths (Auto-Discovered!)
The CLI automatically discovers SPTarkov packages from your NuGet cache and ASP.NET Core from your .NET installation. In most cases, you don't need to specify any --refs:
# That's it! No --refs needed
spt-bridgeui-typegen --assembly dist/MyMod.dll --output frontend/src/api
Use --verbose to see what was auto-discovered:
spt-bridgeui-typegen --assembly dist/MyMod.dll --output frontend/src/api --verbose
# Output:
# 🔍 Auto-discovering references...
# 📂 NuGet cache: C:\Users\you\.nuget\packages
# ✓ sptarkov.server.core (4.0.6)
# ✓ sptarkov.di (4.0.6)
# ✓ ASP.NET Core (9.0.11)
# ...
Manual overrides (rarely needed):
# Add additional reference paths if auto-discovery misses something
spt-bridgeui-typegen --assembly dist/MyMod.dll --output frontend/src/api \
--refs "C:/custom/path/to/assemblies"
# Disable auto-discovery entirely
spt-bridgeui-typegen --assembly dist/MyMod.dll --output frontend/src/api \
--no-auto-refs --refs "C:/my/refs"
Roadmap
- Phase 1: Core Framework - Static serving, SPA fallback, API attributes
- Phase 2: Type Generation - TypeScript types from
[ExportTs]attributes - Phase 3: API Client Generation - Typed fetch functions from
[ApiEndpoint] - Phase 4: Templates -
dotnet newtemplates for React/Vue/Vanilla
License
MIT
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net9.0-windows7.0 is compatible. net10.0-windows was computed. |
-
net9.0-windows7.0
- No dependencies.
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.