It's all done on the server.
I mean, 100 ms between a click and a cross appearing on screen is not great user experience, but it's not even the worst. If you're writing a game, a little client side prediction is a good idea.
But if you have a form with instant validation, or any old regular UI, that is not necessary at all. The only built in optimistic UI functionality on Live View is disabling a button when you press it and wait for the server to respond, to avoid double submissions.
Arguably because you're trusting the client and essentially the built or built-in behavior is therefore optimistic by default. Then hopefully validating on submission server-side.
Though maybe I’m wrong and there has been some new developments to address this, I wasn’t following too closely.
There are also use cases which are a bad fit for LiveView: Animations - animations, menus, and general UI events that do not need the server in the first place are a bad fit for LiveView. Those can be achieved without LiveView in multiple ways, such as with CSS and CSS transitions, using LiveView hooks, or even integrating with UI toolkits designed for this purpose, such as Bootstrap, Alpine.JS, and similar.
https://hexdocs.pm/phoenix_live_view/Phoenix.LiveView.html#m...
[0] https://hexdocs.pm/phoenix_live_view/js-interop.html#simulat...
[1] https://hexdocs.pm/phoenix_live_view/js-interop.html#loading...
But yes as soon as you're on the internet you'll often feel the delay if your app is interactive.
The problem is that it's a bit random, because the network and the VM performances are never totally linear.
I remember implementing a countdown (using 1s send_after()) that would work fine most of the time, but sometimes there would be some hiccup and the countdown would stall just a bit and then process the counter in an accelerated fashion, which was terrible from a UI point of view, so in the end I did it in JS except for the update once the end reached.