For the past decade, the web development industry has pushed toward single-page applications. React, Vue, and Angular became the default choice for almost everything. But a counter-movement is gaining serious momentum — and it’s worth understanding why.
How we got here
In the early web, every page was server-rendered. You clicked a link, the server generated HTML, the browser displayed it. Simple, fast, reliable.
Then Gmail and Google Maps showed what was possible with JavaScript-heavy applications. The industry extrapolated from there: if SPAs work for Gmail, they must work for everything. React launched in 2013, and within a few years, it became the default tool for building any web interface — including simple marketing sites and blogs that had no business being SPAs.
We’re now seeing the consequences of that overcorrection.
The SPA tax
Every SPA pays a tax that server-rendered applications don’t:
Initial load penalty
Before a React application can show anything useful, the browser must download the JavaScript bundle (typically 200-500KB gzipped), parse it, compile it, execute it, fetch data from an API, and then render the page. On a fast connection and a powerful device, this takes 1-2 seconds. On a mid-range phone over 4G, it can take 5-10 seconds.
A server-rendered page sends ready-to-display HTML. The browser starts rendering immediately. Time to first meaningful content is typically under 500ms.
Complexity tax
An SPA is two applications: a JavaScript frontend and an API backend. This means:
- Two codebases to maintain (often in different languages)
- Two deployment pipelines
- State management in two places that must stay synchronised
- Authentication handled across a network boundary
- API versioning and backwards compatibility
- CORS configuration
A server-rendered application is one codebase. State lives in one place. Authentication is handled by sessions. There’s no API to version or CORS to configure.
SEO tax
Search engines can execute JavaScript, but they don’t always do it well or quickly. Google’s JavaScript rendering can be delayed by days or weeks. Other search engines may not render JavaScript at all.
SPAs require additional complexity (server-side rendering, static site generation, or pre-rendering) to be properly indexed. This adds build complexity and potential points of failure.
Server-rendered pages are indexed immediately and reliably. The HTML is right there.
Accessibility tax
SPAs break the browser’s built-in navigation model. Page transitions, focus management, scroll position, browser history, and screen reader announcements all need to be manually reimplemented in JavaScript. Many SPAs get this wrong, creating accessibility barriers for users who rely on assistive technology.
Server-rendered pages work with the browser’s native behaviour. Navigation, focus, and history work correctly by default.
When SPAs genuinely make sense
SPAs aren’t wrong — they’re overused. There are legitimate use cases where client-side rendering is the right choice:
Offline-capable applications. If your app needs to work without an internet connection (a note-taking app, a field data collection tool), you need client-side logic and local storage. An SPA with a service worker is the right approach.
Highly interactive interfaces. Applications like Figma, Google Docs, or Spotify’s web player involve constant, complex client-side interactions — drag-and-drop, real-time collaboration, audio playback. These genuinely need a thick client.
Applications where page reloads are unacceptable. A music player that stops when you navigate. A video editor that loses your timeline. A trading platform where milliseconds matter. These need persistent client-side state.
If your application doesn’t fit one of these categories, you probably don’t need an SPA.
The modern server-rendered approach
Server-rendered doesn’t mean going back to 2005. Modern tools like Phoenix LiveView, Laravel Livewire, and Hotwire bring real-time interactivity to server-rendered applications:
- Real-time updates without page reloads — forms validate instantly, search results appear as you type, notifications arrive in real time
- WebSocket connections that send tiny HTML diffs instead of full page loads
- Server-side state management — no Redux, no Zustand, no state synchronisation bugs
- Instant navigation between pages via WebSocket, not full HTTP round-trips
The user experience is indistinguishable from an SPA. The developer experience is dramatically simpler.
A practical comparison
Consider a typical business application: a dashboard with real-time data, forms with validation, and user authentication.
SPA approach (React + API):
- Frontend: React, React Router, state management library, form library, auth library
- Backend: Node/Python/Ruby API with authentication, authorisation, serialisation
- Infrastructure: two deployments, CORS, API gateway
- Total JavaScript shipped to browser: 300-500KB
- Time to interactive: 2-4 seconds
- Developer hours for equivalent functionality: higher
Server-rendered approach (Phoenix LiveView):
- One Elixir application handling everything
- Real-time updates via WebSocket
- Forms with server-side validation that updates in real time
- Session-based authentication (built-in)
- Total JavaScript shipped to browser: ~30KB
- Time to interactive: under 1 second
- Developer hours for equivalent functionality: lower
Same user experience. Fraction of the complexity.
Making the choice
Ask yourself honestly:
- Does my application need to work offline?
- Does my application involve complex client-side interactions that can’t be expressed as HTML updates?
- Am I choosing an SPA because my project requires it, or because it’s what I’m familiar with?
If the answer to questions 1 and 2 is no, a server-rendered approach will likely give you a better product with less effort.
Our position
We build with Phoenix LiveView because it gives our clients the interactivity of an SPA with the simplicity and performance of server-rendered HTML. It’s not the right tool for every project — but for the web applications and websites we specialise in, it’s the best option available.
Want to discuss which approach fits your project? Get in touch at support@linjerum.com.