⚛️ React hooks for the Octane Rocket League overlay toolkit.
@octane-rl/react wraps @octane-rl/core in a tiny set of ergonomic React hooks so you can build live Rocket League overlays, scoreboards, stat tickers, replay screens, and casting tools without ever touching the Rocket League Stats API. 🎮✨
- 🔌 Auto-managed connection to the Octane WebSocket relay (no boilerplate, no listeners to clean up).
- 🧠 Full game state via
useOctaneState(score, time, players, ball, boost, much more). - 🛰️ Match metadata via
useOctaneMeta(teams, players, arena info). - 🎬 Lifecycle awareness via
useOctaneGameState(idle, live, replay, podium, paused, ended). - 🔔 Typed gameplay events via
useOctaneEvents(goals, ball hits, crossbar pings, statfeed, countdowns, and more). - 🧰 TypeScript-first with full type inference on every event.
- ⚡ React 19 ready, ESM-only, zero config.
npm install @octane-rl/react
# or
pnpm add @octane-rl/react
# or
yarn add @octane-rl/reactℹ️
react@^19is a hard requirement.@octane-rl/coreships alongside as the underlying transport.
Octane-React talks to the Octane-Bridge over a local WebSocket. Make sure:
- 🎯 Rocket League is running.
- 🛜 The bridge is installed and running.
- 🔢 You know the port the bridge is listening on (defaults to
49124).
If you need a custom port, call configureOctane once at app startup, before any hook renders:
// main.tsx
import { configureOctane } from '@octane-rl/react'
configureOctane({ port: 49124 })🪧 Configuration is sticky. After the first hook subscribes, the connection opens and further
configureOctanecalls are ignored (with a warning). 🔒
Streams the latest full game state. Returns null until the first update lands.
import { useOctaneState } from '@octane-rl/react'
export function Scoreboard() {
const state = useOctaneState()
if (!state) {
return <div>⏳ Waiting for Rocket League...</div>
}
return (
<div>
🔵 {state.game.teams[0].score} : {state.game.teams[1].score} 🟠
</div>
)
}Returns the per-match meta (team names, player roster, arena, etc.) once the match initializes.
import { useOctaneMeta } from '@octane-rl/react'
export function MatchHeader() {
const meta = useOctaneMeta()
if (!meta) return null
return <h1>🏟️ {meta.arena}</h1>
}Tip
The meta is provided by the bridge. To update the meta, the Admin Panel is required. Otherwise the metadata is empty and teamnames and logos will be missing.
Reduces the firehose of lifecycle events into a single, friendly enum. Perfect for swapping overlays per phase.
import { useOctaneGameState, GameState } from '@octane-rl/react'
export function OverlayRouter() {
const phase = useOctaneGameState()
switch (phase) {
case GameState.live: return <LiveScoreboard /> // 🟢
case GameState.replay: return <GoalReplayBanner /> // 🎬
case GameState.replayEnding: return <FadeOut /> // 🌫️
case GameState.paused: return <PauseCard /> // ⏸️
case GameState.podium: return <PodiumScreen /> // 🏆
case GameState.ended: return <PostGameStats /> // 📈
case GameState.idle: return <Idle /> // 💤
}
}Possible values: idle 💤, live 🟢, replay 🎬, replayEnding 🌫️, podium 🏆, ended 📈, paused ⏸️.
Subscribe to gameplay events. Two modes:
import { useOctaneEvents } from '@octane-rl/react'
export function EventTicker() {
const event = useOctaneEvents()
return <pre>{event && JSON.stringify(event, null, 2)}</pre>
}import { useOctaneEvents, EventType } from '@octane-rl/react'
export function GoalToast() {
const goal = useOctaneEvents(EventType.goalScored)
if (!goal) return null
return <div>⚽ GOAL by {goal.scorer.name}!</div>
}Available gameplay event types:
| Event 🎟️ | EventType value |
Payload |
|---|---|---|
| ⚽ Goal scored | EventType.goalScored |
GoalScoredEvent |
| 🏀 Ball hit | EventType.ballHit |
BallHitEvent |
| 🥅 Crossbar hit | EventType.crossbarHit |
CrossbarHitEvent |
| 🕒 Clock tick | EventType.clockUpdatedSeconds |
ClockUpdatedEvent |
| 3️⃣ Countdown begin | EventType.countdownBegin |
CountdownBeginEvent |
| 🚦 Round started | EventType.roundStarted |
RoundStartedEvent |
| 📢 Statfeed | EventType.statfeedEvent |
StatFeedEvent (use StatFeedEventType for sub-typing) |
🧹 Lifecycle events (match start/end, replay phases, pause) are filtered out of
useOctaneEvents. UseuseOctaneGameStatefor those.
import {
configureOctane,
useOctaneState,
useOctaneGameState,
useOctaneEvents,
GameState,
EventType,
} from '@octane-rl/react'
configureOctane({ port: 49124 })
export function Overlay() {
const state = useOctaneState()
const phase = useOctaneGameState()
const lastGoal = useOctaneEvents(EventType.goalScored)
if (phase === GameState.idle) return <div>💤 Waiting for kickoff...</div>
return (
<div className="overlay">
<div className="score">
🔵 {state?.game.teams[0].score ?? 0}
<span> : </span>
{state?.game.teams[1].score ?? 0} 🟠
</div>
{phase === GameState.replay && lastGoal && (
<div className="goal-card">⚽ {lastGoal.scorer.name} scored!</div>
)}
</div>
)
}npm run build # 📦 build ESM + .d.ts via tsup
npm run dev # 👀 watch mode
npm run typecheck # 🧐 tsc --noEmit
npm run test # 🧪 jestPRs welcome! 💚 Please run npm run typecheck and npm run test before opening one.
MIT 🆓