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?

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.

  • Much easier... in theory

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.)

Front-end Frameworks & SPAs

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?
  • To separate concerns?
  • You are not Facebook
  • You are not Shopify

Here comes Hotwire

Maybe you just need Turbo with Stimulus...

And https://www.stimulus-components.com/ πŸ˜‡

Basic concept

Turbo Frames

Turbo Frames

            
              <%# app/views/todos/edit.html.erb %>

              

Editing message

<%= turbo_frame_tag @todo do %> <%# form_with ... %> <% 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

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

Turbo Stream

        
          format.turbo_stream do
            render turbo_stream: [
              turbo_stream.append(...),
              turbo_stream.replace(...)
            ]
          end
        
      

Demo

You now πŸ‘‡

Other example

Invoice builder

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

Don't reinvent the wheel

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.

Remember: Nobody cares

  • Backend is boring. Because it's a solved problem.
  • Don't fall into the Hype Driven Development.

Conclusion

  • SPA are great, just use it wisely.
  • Choose the right tool for your (real) needs.
me
@guillaumebriday

Thanks ! πŸ™