UI Overview
True Life's UI system is built with React 19 and the React Compiler for automatic optimization, combined with the observable system for cross-runtime state management. It provides a modern, performant interface for in-game HUDs and pages.
Technology Stack
| Technology | Version | Purpose |
|---|---|---|
| React | 19 | UI framework |
| React Compiler | Latest | Automatic memoization and optimization |
| Observable System | - | State management with cross-runtime sync |
| Tailwind CSS | 3.4 | Utility-first styling |
| shadcn/ui | Latest | Component library (React) |
| Motion | 12.23 | Animations |
| Vite | 7.3 | Build tool |
Architecture
Feature Types
HUDs
HUDs are small overlays positioned in a 3x3 grid:
HUDs are:
- Always visible during gameplay (unless group is hidden)
- Positioned via
hudPositionproperty - Stacked by
hudPrioritywithin a position - Grouped via
hudGroupfor visibility control
Pages
Pages are full-screen interfaces:
- Open/close via client events
- Block game input when visible
- Used for menus, inventories, etc.
Project Structure
ui/
├── src/
│ ├── main.tsx # React app entry point
│ ├── modules/index.ts # Feature discovery from MODULES
│ ├── feature.ts # HudDefinition, PageDefinition, HUD groups
│ ├── shells/ # Layout containers
│ │ ├── HudShell/ # 3x3 grid HUD container
│ │ └── PageShell/ # Full-screen container
│ ├── components/ # Reusable components
│ │ └── ui/ # shadcn/ui components (React)
│ └── lib/ # Utilities
│ ├── rpc.ts # Web RPC (global.rpc for web)
│ └── telemetry/ # Web telemetry
├── vite.config.ts
└── tailwind.config.cjsDevelopment Server
Start the UI dev server:
pnpm dev:ui
# Runs at http://127.0.0.1:41524The dev server provides:
- Fast refresh for React components
- Mock NUI events for testing
- React DevTools support
Path Aliases
| Alias | Path |
|---|---|
@ui/* | ./ui/src/* |
@core/* | ./src/lib/* |
@modules/* | ./src/modules/* |
Key Concepts
Services and Observables
UI state is managed through services and webOwned observables:
// In a service
export class MyService {
public hudState = webOwned<HudState>({
id: "my:hud",
initialValue: { data: null },
});
}
// In a component
import { useService, useObservable } from "@core/react";
function MyComponent() {
const myService = useService(MyService);
const state = useObservable(myService.hudState);
return <div>{state.data}</div>;
}HUD Groups
Control visibility of HUD clusters:
import { setHudGroupVisible, isHudGroupVisible } from "@ui/feature";
// Hide all HUDs in the "default" group
setHudGroupVisible("default", false);
// Check visibility
if (isHudGroupVisible("default")) {
// HUDs are visible
}Best Practices
- Use webOwned for UI state - Not Redux, use observables
- Access services via useService - Dependency injection for consistency
- Use useObservable for reactivity - Auto-subscription and cleanup
- Use Tailwind utilities - Consistent styling
- Leverage shadcn/ui - Pre-built accessible components
- Add animations - CSS animations or Motion library
- Use HUD groups - For fullscreen overlays that need to hide HUDs
- Let React Compiler optimize - Avoid manual useMemo/useCallback unless necessary