Slowly we are rewriting individual pieces as embedded React components (no SPA here) and moving to a proper API layer that the components talk to. The separation of concern has made it a loot easier to increase code coverage and ensure a controlled rollout of new features.
We also just bit the bullet and paid for syncfusion to use on our frontend to avoid reinventing the wheel for a lot of the functionality we need.