So you need to pay some attention to applying-css/hooking-into-the-tooling (phx-click-loading) to show that a button was indeed clicked and is working on fetching data.
Some things such as flipping between tabs can feel quite bad if you click and the tab-button just puts a spinner there or the button just "fades a bit". Its much nicer to replace the content pane with some mock-blocks to show "yes, the tab swapped and its loading data" which is where you need JS to temporarily hide/show some things.
And once you start doing that you should really be measuring the avg rtt on the socket because showing the mock-content for 1ms then swapping the real content is also bad, so you need JS again to figure the best UX for that client.
Or you simply render all the tabs in one go and again, duck into JS to perform the local show/hide.