diff --git a/liwords-ui/src/lobby/login.tsx b/liwords-ui/src/lobby/login.tsx
index 9171ad11f..5f6670668 100644
--- a/liwords-ui/src/lobby/login.tsx
+++ b/liwords-ui/src/lobby/login.tsx
@@ -11,7 +11,7 @@ import { toAPIUrl } from '../api/api';
 
 export const Login = React.memo(() => {
   const { useState } = useMountedState();
-  const { resetStore } = useResetStoreContext();
+  const { resetLoginStateStore } = useResetStoreContext();
 
   const [err, setErr] = useState('');
   const [loggedIn, setLoggedIn] = useState(false);
@@ -42,9 +42,9 @@ export const Login = React.memo(() => {
 
   React.useEffect(() => {
     if (loggedIn) {
-      resetStore();
+      resetLoginStateStore();
     }
-  }, [loggedIn, resetStore]);
+  }, [loggedIn, resetLoginStateStore]);
 
   return (
     
diff --git a/liwords-ui/src/settings/settings.tsx b/liwords-ui/src/settings/settings.tsx
index b0943d206..86b4bcd6e 100644
--- a/liwords-ui/src/settings/settings.tsx
+++ b/liwords-ui/src/settings/settings.tsx
@@ -80,7 +80,7 @@ export const Settings = React.memo((props: Props) => {
   const { loginState } = useLoginStateStoreContext();
   const { userID, username: viewer, loggedIn } = loginState;
   const { useState } = useMountedState();
-  const { resetStore } = useResetStoreContext();
+  const { resetLoginStateStore } = useResetStoreContext();
   const { section } = useParams();
   const [category, setCategory] = useState(
     getInitialCategory(section, loggedIn)
@@ -170,13 +170,13 @@ export const Settings = React.memo((props: Props) => {
           message: 'Success',
           description: 'You have been logged out.',
         });
-        resetStore();
+        resetLoginStateStore();
         history.push('/');
       })
       .catch((e) => {
         console.log(e);
       });
-  }, [history, resetStore]);
+  }, [history, resetLoginStateStore]);
 
   const updatedAvatar = useCallback(
     (avatarUrl: string) => {
diff --git a/liwords-ui/src/socket/socket.ts b/liwords-ui/src/socket/socket.ts
index 46ce9d32f..76c6ca1ff 100644
--- a/liwords-ui/src/socket/socket.ts
+++ b/liwords-ui/src/socket/socket.ts
@@ -1,4 +1,11 @@
-import { useCallback, useEffect, useMemo, useRef } from 'react';
+import {
+  createContext,
+  useCallback,
+  useContext,
+  useEffect,
+  useMemo,
+  useRef,
+} from 'react';
 import axios from 'axios';
 import jwt from 'jsonwebtoken';
 import useWebSocket from 'react-use-websocket';
@@ -7,7 +14,6 @@ import { message } from 'antd';
 import { useMountedState } from '../utils/mounted';
 import { useLoginStateStoreContext } from '../store/store';
 import {
-  useOnSocketMsg,
   ReverseMessageType,
   enableShowSocket,
   parseMsgs,
@@ -18,6 +24,42 @@ import { ActionType } from '../actions/actions';
 import { reloadAction } from './reload';
 import { birthdateWarning } from './birthdateWarning';
 
+// Store-specific code.
+
+const defaultFunction = () => {};
+
+export type LiwordsSocketValues = {
+  sendMessage: (msg: Uint8Array) => void;
+  justDisconnected: boolean;
+};
+
+export type OnSocketMsgType = (reader: FileReader) => void;
+
+type LiwordsSocketStoreData = {
+  liwordsSocketValues: LiwordsSocketValues;
+  onSocketMsg: OnSocketMsgType;
+  resetLiwordsSocketStore: () => void;
+  setLiwordsSocketValues: React.Dispatch<
+    React.SetStateAction
+  >;
+  setOnSocketMsg: React.Dispatch>;
+};
+
+export const LiwordsSocketContext = createContext({
+  liwordsSocketValues: {
+    sendMessage: defaultFunction,
+    justDisconnected: false,
+  },
+  onSocketMsg: defaultFunction,
+  resetLiwordsSocketStore: defaultFunction,
+  setLiwordsSocketValues: defaultFunction,
+  setOnSocketMsg: defaultFunction,
+});
+
+export const useLiwordsSocketContext = () => useContext(LiwordsSocketContext);
+
+// Non-Store code follows.
+
 const getSocketURI = (): string => {
   const loc = window.location;
   let protocol;
@@ -51,23 +93,36 @@ type DecodedToken = {
 // Returning undefined from useEffect is fine, but some linters dislike it.
 const doNothing = () => {};
 
-export const LiwordsSocket = (props: {
-  resetSocket: () => void;
-  setValues: (_: {
-    sendMessage: (msg: Uint8Array) => void;
-    justDisconnected: boolean;
-  }) => void;
-}): null => {
+export const LiwordsSocket = (props: {}): null => {
   const isMountedRef = useRef(true);
   useEffect(() => () => void (isMountedRef.current = false), []);
   const { useState } = useMountedState();
 
-  const { resetSocket, setValues } = props;
-  const onSocketMsg = useOnSocketMsg();
+  const {
+    onSocketMsg,
+    resetLiwordsSocketStore,
+    setLiwordsSocketValues,
+  } = useLiwordsSocketContext();
 
   const loginStateStore = useLoginStateStoreContext();
   const location = useLocation();
-  const { pathname } = location;
+  const pathname = useMemo(() => {
+    const originalPathname = location.pathname;
+    // XXX: The socket requires path to know which realms it has to connect to.
+    // See liwords-socket pkg/hub/hub.go RegisterRealm.
+    // That calls back into liwords pkg/bus/bus.go handleNatsRequest.
+
+    // It seems only a few paths matter.
+    if (
+      originalPathname.startsWith('/game/') ||
+      originalPathname.startsWith('/tournament/') ||
+      originalPathname.startsWith('/club/')
+    )
+      return originalPathname;
+
+    // For everything else, there's MasterCard.
+    return '/';
+  }, [location.pathname]);
 
   // const [socketToken, setSocketToken] = useState('');
   const [justDisconnected, setJustDisconnected] = useState(false);
@@ -109,8 +164,6 @@ export const LiwordsSocket = (props: {
           userID: decoded.uid,
           loggedIn: decoded.a,
           connID: cid,
-          isChild: decoded.cs,
-          path: pathname,
           perms: decoded.perms?.split(','),
         },
       });
@@ -213,12 +266,21 @@ export const LiwordsSocket = (props: {
   useEffect(() => {
     const t = setTimeout(() => {
       console.log('reconnecting socket');
-      resetSocket();
+      resetLiwordsSocketStore();
     }, 15000);
     return () => {
       clearTimeout(t);
     };
-  }, [patienceId, resetSocket]);
+  }, [patienceId, resetLiwordsSocketStore]);
+
+  // Force reconnection when pathname materially changes.
+  const knownPathname = useRef(pathname); // Remember the pathname on first render.
+  const isCurrentPathname = knownPathname.current === pathname;
+  useEffect(() => {
+    if (!isCurrentPathname) {
+      resetLiwordsSocketStore();
+    }
+  }, [isCurrentPathname, resetLiwordsSocketStore]);
 
   const { sendMessage: originalSendMessage } = useWebSocket(
     getFullSocketUrlAsync,
@@ -271,8 +333,8 @@ export const LiwordsSocket = (props: {
     justDisconnected,
   ]);
   useEffect(() => {
-    setValues(ret);
-  }, [setValues, ret]);
+    setLiwordsSocketValues(ret);
+  }, [setLiwordsSocketValues, ret]);
 
   return null;
 };
diff --git a/liwords-ui/src/store/login_state.ts b/liwords-ui/src/store/login_state.ts
index ab0a8b8d7..ab70d9c92 100644
--- a/liwords-ui/src/store/login_state.ts
+++ b/liwords-ui/src/store/login_state.ts
@@ -1,21 +1,15 @@
 import { Action, ActionType } from '../actions/actions';
 
-export type LoginState = {
+export type AuthInfo = {
   username: string;
   userID: string;
   loggedIn: boolean;
   connID: string;
-  connectedToSocket: boolean;
-  path: string;
   perms: Array;
 };
 
-export type AuthInfo = {
-  username: string;
-  userID: string;
-  loggedIn: boolean;
-  connID: string;
-  perms: Array;
+export type LoginState = AuthInfo & {
+  connectedToSocket: boolean;
 };
 
 export function LoginStateReducer(
diff --git a/liwords-ui/src/store/socket_handlers.ts b/liwords-ui/src/store/socket_handlers.ts
index ad84d5127..bc4cba209 100644
--- a/liwords-ui/src/store/socket_handlers.ts
+++ b/liwords-ui/src/store/socket_handlers.ts
@@ -163,6 +163,8 @@ export const ReverseMessageType = (() => {
   return ret;
 })();
 
+// This needs to have access to Rest Of Store.
+// Therefore it cannot be used directly from LiwordsSocket.
 export const useOnSocketMsg = () => {
   const { challengeResultEvent } = useChallengeResultEventStoreContext();
   const { addChat, deleteChat } = useChatStoreContext();
diff --git a/liwords-ui/src/store/store.tsx b/liwords-ui/src/store/store.tsx
index 0519de30a..f3ddde52f 100644
--- a/liwords-ui/src/store/store.tsx
+++ b/liwords-ui/src/store/store.tsx
@@ -29,6 +29,14 @@ import {
 } from './reducers/tournament_reducer';
 import { MetaEventState, MetaStates } from './meta_game_events';
 import { StandardEnglishAlphabet } from '../constants/alphabets';
+import {
+  LiwordsSocket,
+  LiwordsSocketContext,
+  LiwordsSocketValues,
+  OnSocketMsgType,
+  useLiwordsSocketContext,
+} from '../socket/socket';
+import { useOnSocketMsg } from './socket_handlers';
 import { SeekRequest } from '../gen/api/proto/ipc/omgseeks_pb';
 import { ServerChallengeResultEvent } from '../gen/api/proto/ipc/omgwords_pb';
 
@@ -243,7 +251,6 @@ const LoginStateContext = createContext({
     loggedIn: false,
     connectedToSocket: false,
     connID: '',
-    path: '',
     perms: [],
   },
   dispatchLoginState: defaultFunction,
@@ -759,9 +766,87 @@ const ExaminableStore = ({ children }: { children: React.ReactNode }) => {
   return ;
 };
 
-// The Real Store.
+// The Real LoginState Store.
 
-const RealStore = ({ children, ...props }: Props) => {
+const RealLoginStateStore = ({ children, ...props }: Props) => {
+  const { useState } = useMountedState();
+
+  const [loginState, setLoginState] = useState({
+    username: '',
+    userID: '',
+    loggedIn: false,
+    connectedToSocket: false,
+    connID: '',
+    perms: new Array(),
+  });
+  const dispatchLoginState = useCallback(
+    (action) => setLoginState((state) => LoginStateReducer(state, action)),
+    []
+  );
+
+  const loginStateStore = useMemo(
+    () => ({
+      loginState,
+      dispatchLoginState,
+    }),
+    [loginState, dispatchLoginState]
+  );
+
+  return (
+    
+  );
+};
+
+// The Real LiwordsSocket Store.
+
+const RealLiwordsSocketStore = ({
+  resetLiwordsSocketStore,
+  children,
+  ...props
+}: Props & {
+  resetLiwordsSocketStore: () => void;
+}) => {
+  const { useState } = useMountedState();
+
+  const [onSocketMsg, setOnSocketMsg] = useState(
+    () => defaultFunction
+  );
+
+  const [liwordsSocketValues, setLiwordsSocketValues] = useState<
+    LiwordsSocketValues
+  >({
+    sendMessage: defaultFunction,
+    justDisconnected: false,
+  });
+
+  const liwordsSocketStore = useMemo(
+    () => ({
+      liwordsSocketValues,
+      onSocketMsg,
+      resetLiwordsSocketStore,
+      setLiwordsSocketValues,
+      setOnSocketMsg,
+    }),
+    [
+      liwordsSocketValues,
+      onSocketMsg,
+      resetLiwordsSocketStore,
+      setLiwordsSocketValues,
+      setOnSocketMsg,
+    ]
+  );
+
+  return (
+    
+  );
+};
+
+// The Real Rest Of Store.
+
+const RealRestOfStore = ({ children, ...props }: Props) => {
   const { useState } = useMountedState();
 
   const clockController = useRef(null);
@@ -788,19 +873,6 @@ const RealStore = ({ children, ...props }: Props) => {
     (action) => setLobbyContext((state) => LobbyReducer(state, action)),
     []
   );
-  const [loginState, setLoginState] = useState({
-    username: '',
-    userID: '',
-    loggedIn: false,
-    connectedToSocket: false,
-    connID: '',
-    path: '',
-    perms: new Array(),
-  });
-  const dispatchLoginState = useCallback(
-    (action) => setLoginState((state) => LoginStateReducer(state, action)),
-    []
-  );
 
   const [tournamentContext, setTournamentContext] = useState(
     defaultTournamentState
@@ -978,13 +1050,6 @@ const RealStore = ({ children, ...props }: Props) => {
     }),
     [lobbyContext, dispatchLobbyContext]
   );
-  const loginStateStore = useMemo(
-    () => ({
-      loginState,
-      dispatchLoginState,
-    }),
-    [loginState, dispatchLoginState]
-  );
   const tournamentStateStore = useMemo(
     () => ({
       tournamentContext,
@@ -1156,7 +1221,6 @@ const RealStore = ({ children, ...props }: Props) => {
     
   );
   ret = ;
-  ret = ;
   ret = ;
   ret = (
     
@@ -1206,30 +1270,87 @@ const RealStore = ({ children, ...props }: Props) => {
   return ;
 };
 
-const ResetStoreContext = createContext({ resetStore: defaultFunction });
+// This needs to be nested inside the Rest Of Store.
+
+const InstallOnSocketMsg = ({ children }: { children: React.ReactNode }) => {
+  const { onSocketMsg, setOnSocketMsg } = useLiwordsSocketContext();
+
+  const newOnSocketMsg = useOnSocketMsg();
+
+  const oldOnSocketMsgRef = useRef(onSocketMsg);
+  oldOnSocketMsgRef.current = onSocketMsg;
+
+  React.useEffect(() => {
+    const old = oldOnSocketMsgRef.current;
+    setOnSocketMsg(() => newOnSocketMsg);
+    return () => {
+      setOnSocketMsg(() => old);
+    };
+  }, [newOnSocketMsg, setOnSocketMsg]);
+
+  return ;
+};
+
+const ResetStoreContext = createContext({
+  resetLoginStateStore: defaultFunction,
+  resetRestOfStore: defaultFunction,
+});
 export const useResetStoreContext = () => useContext(ResetStoreContext);
 
+// Now includes the Socket.
+
 export const Store = ({ children }: { children: React.ReactNode }) => {
   const { useState } = useMountedState();
 
   // In JS the | 0 loops within int32 and avoids reaching Number.MAX_SAFE_INTEGER.
-  const [storeId, setStoreId] = useState(0);
-  const resetStore = useCallback(() => setStoreId((n) => (n + 1) | 0), []);
+  const [loginStateStoreId, setLoginStateStoreId] = useState(0);
+  const resetLoginStateStore = useCallback(
+    () => setLoginStateStoreId((n) => (n + 1) | 0),
+    []
+  );
+  const [liwordsSocketStoreId, setLiwordsSocketStoreId] = useState(0);
+  const resetLiwordsSocketStore = useCallback(
+    () => setLiwordsSocketStoreId((n) => (n + 1) | 0),
+    []
+  );
+  const [restOfStoreId, setRestOfStoreId] = useState(0);
+  const resetRestOfStore = useCallback(
+    () => setRestOfStoreId((n) => (n + 1) | 0),
+    []
+  );
 
   // Reset on browser navigation.
   React.useEffect(() => {
     const handleBrowserNavigation = (evt: PopStateEvent) => {
-      resetStore();
+      resetRestOfStore();
     };
     window.addEventListener('popstate', handleBrowserNavigation);
     return () => {
       window.removeEventListener('popstate', handleBrowserNavigation);
     };
-  }, [resetStore]);
+  }, [resetRestOfStore]);
+
+  const resetStore = useMemo(
+    () => ({
+      resetLoginStateStore,
+      resetRestOfStore,
+    }),
+    [resetLoginStateStore, resetRestOfStore]
+  );
 
   return (
-    
-      
+    
+      
+        
+          
+          
+            {children}
+          
+        
+      
     
   );
 };
diff --git a/liwords-ui/src/topbar/topbar.tsx b/liwords-ui/src/topbar/topbar.tsx
index 6ee0a01fd..e3e824836 100644
--- a/liwords-ui/src/topbar/topbar.tsx
+++ b/liwords-ui/src/topbar/topbar.tsx
@@ -81,7 +81,7 @@ export const TopBar = React.memo((props: Props) => {
 
   const { currentLagMs } = useLagStoreContext();
   const { loginState } = useLoginStateStoreContext();
-  const { resetStore } = useResetStoreContext();
+  const { resetLoginStateStore } = useResetStoreContext();
   const { tournamentContext } = useTournamentStoreContext();
   const { username, loggedIn, connectedToSocket } = loginState;
   const [loginModalVisible, setLoginModalVisible] = useState(false);
@@ -97,7 +97,7 @@ export const TopBar = React.memo((props: Props) => {
           message: 'Success',
           description: 'You have been logged out.',
         });
-        resetStore();
+        resetLoginStateStore();
       })
       .catch((e) => {
         console.log(e);
diff --git a/liwords-ui/src/tournament/room.tsx b/liwords-ui/src/tournament/room.tsx
index eef06e476..1375b269b 100644
--- a/liwords-ui/src/tournament/room.tsx
+++ b/liwords-ui/src/tournament/room.tsx
@@ -1,6 +1,7 @@
 import React from 'react';
 
 import { useCallback, useMemo } from 'react';
+import { useLocation } from 'react-router-dom';
 
 import {
   useLoginStateStoreContext,
@@ -35,7 +36,8 @@ export const TournamentRoom = (props: Props) => {
   const { competitorState: competitorContext } = tournamentContext;
   const { isRegistered } = competitorContext;
   const { sendSocketMsg } = props;
-  const { path } = loginState;
+  const location = useLocation();
+  const path = location.pathname;
   const [badTournament, setBadTournament] = useState(false);
   const [selectedGameTab, setSelectedGameTab] = useState('GAMES');