This is exactly the thinking behind Phoenix LiveView, except that I'd argue that it's even easier in that case. You write an app the looks like a normal server-rendered HTML page, but having served the page it then opens a websocket connection to the server, down which UI events are sent and HTML updates come back the other way.
Add in a little AlpineJS to do basic client side interactivity (show/hide something, flick between tabs etc.) and you have a rich interactive app, that works responsively and requires almost no JS. It's also much easier to test the whole system.
The main downside to SPA is that it's not quite as well set up to do major UI stuff without going via the server. It's not the right tool for offline apps, or things like that. But for UI updates where the SPA has to query data from the server it's exactly the same and often sends less data because it only sends what it needs to (the HTML diffing and compression is very efficient).
Add in a little AlpineJS to do basic client side interactivity (show/hide something, flick between tabs etc.) and you have a rich interactive app, that works responsively and requires almost no JS. It's also much easier to test the whole system.
The main downside to SPA is that it's not quite as well set up to do major UI stuff without going via the server. It's not the right tool for offline apps, or things like that. But for UI updates where the SPA has to query data from the server it's exactly the same and often sends less data because it only sends what it needs to (the HTML diffing and compression is very efficient).