I have a deep Proxy that wraps the game state and watches for any changes on the server. It then sends a diff of the current state with the previous synced one using fast-json-patch (https://www.npmjs.com/package/fast-json-patch) to the client which also uses the same library to reconcile the state within a Vue reactive({}) object, which then updates the DOM. Since it's only a card game, it's all event based and there's no game loop or polling needed and there's only a few websocket messages per minute. It was fun to design and is pretty efficient too.