Every interactive surface in the Pokémon Showdown client — the main menu, chat rooms, battle screens, the teambuilder, the ladder — is a room. Each room is represented by two objects that live side-by-side: aDocumentation Index
Fetch the complete documentation index at: https://mintlify.com/smogon/pokemon-showdown-client/llms.txt
Use this file to discover all available pages before exploring further.
PSRoom model that holds state and handles server messages, and a PSRoomPanel Preact component that renders that state to the DOM. This model-view split is central to PS’s architecture and makes it straightforward to add new panel types without touching the core layout engine.
PSRoom: the model base class
PSRoom extends PSStreamModel<Args | null> and is defined in client-main.ts. It emits Args tuples when the server sends the room a message, and null to signal a generic re-render (e.g. after a preference change).
Room locations
| Location | Description |
|---|---|
'left' | Main left panel in two-panel mode; the only visible panel in one-panel mode |
'right' | Right panel in two-panel mode (e.g. the Rooms list, a chat room) |
'popup' | Non-blocking floating overlay (e.g. user card) |
'modal-popup' | Blocking modal overlay (e.g. login, options) |
'mini-window' | Compact embedded widget shown inside the main menu panel |
PSRoomPanel: the view base class
PSRoomPanel<T extends PSRoom> is defined in panels.tsx and extends preact.Component<{ room: T }>. Every panel receives its room model as the room prop.
PSRoomPanel subscribes to its room in componentDidMount and unsubscribes all subscriptions in componentWillUnmount. The shouldComponentUpdate override skips re-renders when the panel is off-screen — an important optimisation when many rooms are open simultaneously.The standard pairing pattern
Every panel type follows the same three-step pattern:Define the model
Subclass
PSRoom, override classType, and implement receiveLine to handle server messages.PSRouter
PSRouter maps browser URLs to room IDs and keeps the browser history in sync with the focused room.
URL extraction rules
extractRoomID handles a wide variety of URL forms and normalises them to a bare room ID:
URLs matching common redirects like
faq, rules, privacy, dex, and credits return null — the router lets those navigate away from the client entirely.Layout constants
panels.tsx to calculate panel widths and trigger mobile-specific scroll behaviour.
Built-in panel types
MainMenuRoom
classType:
'mainmenu' · location: leftThe home screen. Shows the main action buttons, news mini-window, and search/challenge UI. Always present as PS.mainmenu.ChatRoom / ChatPanel
classType:
'chat' · location: leftGeneral-purpose chat rooms and private messages. Hosts BattleLog for message rendering and manages the user list.BattleRoom / BattlePanel
classType:
'battle' · location: left (or right with rightpanelbattles)Extends ChatRoom. Hosts a Battle instance and a BattleScene. The chat log and battle animation share the same panel.BattlesRoom / BattlesPanel
classType:
'battles' · location: rightShows the list of ongoing battles the user can spectate. Lives in the right panel by default.TeambuilderRoom
panel id:
'teambuilder' · location: leftThe team editor. Manages curFolder, exportMode, and delegates to PS.teams for persistence. See Teambuilder for details.LadderFormatRoom
classType:
'ladder' · location: leftDisplays the leaderboard for a specific format. Fetches data from the PS ladder API via Net.How to extend PSRoom with a custom room type
The noURL flag
Settingstatic noURL = true on a panel subclass (or passing noURL: true in PS.addRoom()) tells PSRouter not to generate a browser history entry when this room is focused. This is used for modal popups (login, options, avatars) that should not be bookmarkable or affect the back button.
