Skip to content

With Local State

The withLocalState hook provides scene-specific state management that automatically cleans up when the scene is destroyed. It’s perfect for temporary state that should reset when switching scenes—like player health, enemy positions, or UI state.

Just like React hooks, withLocalState uses keys as singleton references:

  • First call: Creates the state with the initial value
  • Subsequent calls: Returns the existing state (initial value is ignored)
  • Same key = same state instance across your entire scene

This means you can call the same hook from:

  • ✅ Your scene’s create() method
  • ✅ Child components (GameObjects, Containers)
  • ✅ Systems and managers
  • ✅ Event handlers

They all share the same state!

class GameScene extends Phaser.Scene {
create(): void {
const player1 = withLocalState(this, 'player', { hp: 100 });
player1.set({ hp: 50 });
// Later, somewhere else in the scene...
const player2 = withLocalState(this, 'player', { hp: 999 }); // Initial value ignored!
console.log(player2.get().hp); // 50 (not 999!)
}
}

When the scene is destroyed (or you call this.scene.stop()), withLocalState automatically:

  • ✅ Removes all event listeners
  • ✅ Clears the state data
  • ✅ Prevents memory leaks

No manual cleanup needed! (Though you can still call clearListeners() if you want.)


Instead of repeating the key and initial state everywhere, create a custom hook:

hooks/withPlayer.ts
import { type HookState, withLocalState } from 'phaser-hooks';
type Player = {
hp: number;
maxHp: number;
level: number;
exp: number;
};
type PlayerState = HookState<Player> & {
takeDamage: (amount: number) => void;
heal: (amount: number) => void;
levelUp: () => void;
};
export function withPlayer(scene: Phaser.Scene): PlayerState {
const state = withLocalState<Player>(scene, 'player', {
hp: 100,
maxHp: 100,
level: 1,
exp: 0,
});
return {
...state,
takeDamage: (amount: number) => {
const current = state.get();
state.patch({ hp: Math.max(0, current.hp - amount) });
},
heal: (amount: number) => {
const current = state.get();
state.patch({ hp: Math.min(current.maxHp, current.hp + amount) });
},
levelUp: () => {
const current = state.get();
state.patch({
level: current.level + 1,
maxHp: current.maxHp + 10,
hp: current.maxHp + 10,
exp: 0,
});
},
};
}

Now you can use withPlayer anywhere in your scene:

GameScene.ts
import { withPlayer } from './hooks/withPlayer';
class GameScene extends Phaser.Scene {
create() {
const player = withPlayer(this);
// Use clean API
player.takeDamage(30);
player.heal(10);
player.levelUp();
// Listen to changes
player.on('change', (newPlayer, oldPlayer) => {
console.log(`HP: ${newPlayer.hp}/${newPlayer.maxHp}`);
if (newPlayer.hp === 0) {
this.scene.start('GameOverScene');
}
});
}
}
// HealthBar.ts - Access the SAME state!
class HealthBar extends Phaser.GameObjects.Container {
constructor(scene: Phaser.Scene) {
super(scene, 0, 0);
const player = withPlayer(scene); // Same player state!
player.on('change', (newPlayer) => {
this.updateBar(newPlayer.hp, newPlayer.maxHp);
});
}
updateBar(hp: number, maxHp: number) {
// Update visual representation
}
}

Try out the with local state example in the interactive demo below:

Click to load the interactive demo


FeatureDescription
Scene-specificState exists only while the scene is active
Automatic cleanupListeners and data removed when scene is destroyed
IsolatedEach scene instance has its own independent state
Singleton patternSame key returns the same state instance
Type-safeFull TypeScript support with type inference

Use withLocalState for:

  • Player state in a gameplay scene
  • Enemy AI state
  • Level-specific data (current wave, score)
  • Temporary UI state (menu selection)
  • Game state that resets per scene

Don’t use withLocalState for:

  • Settings (use withGlobalState)
  • User profile (use withPersistentState)
  • Data that persists across scenes

function withLocalState<T>(
scene: Phaser.Scene,
key: string,
initialValue: T,
options?: {
debug?: boolean;
validator?: (value: T) => true | string;
}
): HookState<T>
  • scene - The Phaser scene instance
  • key - Unique identifier for this state (singleton reference)
  • initialValue - Initial state value (used only on first call)
  • options (optional)
    • debug - Enable console logging for debugging
    • validator - Validation function for state updates

A HookState<T> object with methods: get(), set(), patch(), on(), once(), off(), clearListeners().