Why you (really) don't need SPA
By Guillaume Briday
Before starting
Quick introduction
Not a silver bullet
Know your context
Why SPA?
More interactive applications
More features to ship
Reduce the overall complexity?
To reduce scalability issues?
Improve productivity / Reduce bugs
Bigger teams
Front-end Frameworks & SPAs
Render happens in the browser
Fetch data in JSON via AJAX
DOM matches the data
React component
import { useState } from 'react'
function MyComponent() {
const [name, setName] = useState('')
const [items, setItems] = useState([])
useEffect(() => {
fetch(`/my/awesome/api?name=${name}`)
.then(res => res.json())
.then((data) => {
setItems(data)
})
}, [name])
return (
...
{items.map(item => (
{item.name}
))}
)
}
From manipulating DOM with jQuery to manipulate Data with React.
How does it work?
Front-end Frameworks & SPAs
Front-end Frameworks & SPAs
Front-end Frameworks & SPAs
Incredible hidden complexity
Business Logic Duplication
Overall performance issues
Complex toolchain (Bundle splitting, Pre-rendering, Webpack, etc)
API and data interchanges
Organizational challenges
Tons of JS (Next, Nuxt, Remix, etc.)
Form validation for example / Carts / Invoices / etc
You still depend on backend anyway, sort, sum, etc
Monitoring performance, network latency, multiple servers, tons of JS, initial load
Microservices hell
Who is responsible to do what, how to recruit
From class dependencies to services dependencies
We all face the same challenges
Front-end Frameworks & SPAs
"It's smooth"
"It separates concerns"
"For scalability"
But why?
To prevent full page reload?
To add interactive actions? (likes, comments, charts, etc)
To refresh parts of the page dynamically?
For scalability issues?
For organizational issues?
To separate concerns?
You are not Shopify
Here comes Hotwire
Basic concept
Turbo Frames
Turbo Frames
<%# app/views/todos/index.html.erb %>
Todos
<%= turbo_frame_tag 'todos_list' do %>
<%= link_to 'All', todos_path %>
<%= link_to 'Completed', todos_path(completed: true) %>
<%= link_to 'In progress', todos_path(completed: false) %>
<% end %>
See: Modern Datatables
Turbo Frames
class CommentsController < ApplicationController
def index
budget = Budget.find(params[:budget_id])
render partial: 'comments/comments',
locals: { comments: budget.comments }
end
end
<%= turbo_frame_tag :comments,
src: comments_path(budget_id: @budget.id),
loading: 'lazy' do %>
<%# Adding a nice loader that will be replaced once the partial gets loaded. %>
<% end %>
Turbo Frames
Update just what you need
No JavaScript required
Basically instant
Everything stay on the server side
Back-end dependent
Progressive enhancement is possible
Plug'n'play
You now π
Turbo 8
Morphing
Scroll position
Broadcast
instaClick
Morphing
<%= turbo_refreshes_with method: :morph, scroll: :preserve %>
Turbo 7
Turbo 8
Don't reinvent the wheel
GraphQL
React SSR (SEO, Speed, db calls)
Fetch / Link / Routers
State managers
Form & AJAX libraries
Only half of your app
Most of new tools are here to fix problems that didn't exist before
Most of them are VC funded
Remember: Nobody cares
Most website have page reload
High interactivity websites are really rare
Focus on your Time to Market
And on your customers needs
Customers don't care. You don't either.
Make your app fast and add cache.
Most of these libraries are VC funded
Remember: Nobody cares
Backend is boring. Because it's a solved problem.
Don't fall into the Hype Driven Development.
Business first.
Is it 10x better?
It's a human problem, not a technical one.
Conclusion
SPA are great, just use it wisely.
Choose the right tool for your (real) needs.
Building software is really (really) hard.
Stick to what has been proven to work.