What is a Single Page Application (SPA)?
A Single Page Application (SPA) is a web app that loads a single HTML page and dynamically updates content via JavaScript as the user interacts — without full page reloads. Navigation between "pages" happens client-side: the URL changes, the relevant component renders, but the browser doesn't fetch a new HTML document.
SPAs feel snappy and app-like (Gmail, Trello, Notion are all SPAs). They trade off two things for that smoothness: slower initial load (entire JS bundle must download before anything works) and harder SEO (crawlers see empty HTML until JS executes).
How SPAs work
- Browser requests
example.com - Server returns minimal HTML + a JS bundle reference
- Browser downloads + executes JS bundle
- JS renders the home view
- User clicks a link → JS intercepts → updates URL + renders new view (no server round-trip)
- Data fetched via API as needed
SPA vs MPA (Multi-Page App)
| Aspect | SPA | MPA (traditional) |
|---|---|---|
| Page navigation | JS-driven, no reload | Server returns full HTML |
| Initial load | Slow (JS bundle) | Fast (HTML only) |
| Subsequent navigation | Instant | Slower (HTTP round-trip) |
| SEO out-of-box | Bad (empty HTML) | Good |
| Browser back/forward | Manual handling needed | Native |
| State management | Client-side (Redux, Pinia) | Server sessions |
| Best for | App-like UIs (dashboard) | Content sites (blog, news) |
Popular SPA frameworks
| Framework | Notes |
|---|---|
| React + React Router | Most popular; library not framework |
| Angular | Full framework, opinionated, TypeScript-first |
| Vue + Vue Router | Approachable, growing |
| Svelte (with SvelteKit SPA mode) | Compile-time framework |
| Solid | React-like, finer reactivity |
| Ember | Convention-heavy, mature |
| Backbone (legacy) | Pioneered SPAs; mostly historical |
SPA pros and cons
| Pros | Cons |
|---|---|
| App-like, smooth UX | Slow first load (large JS bundle) |
| Fast subsequent navigation | SEO requires extra work (SSR/prerender) |
| Decoupled frontend/backend | Browser back/forward needs handling |
| Reusable APIs (mobile uses same) | JS error breaks entire app |
| Reduced server load (server just serves API) | Initial bundle must include router, state, all routes |
When to use an SPA
- Logged-in dashboards. No SEO need; users tolerate first-load wait.
- Real-time apps. Chat, collab tools, live data.
- Highly interactive UIs. Drag-drop, complex state.
- Mobile-like web apps. Wants to feel native.
When NOT to use an SPA
- Content-heavy sites. Blogs, marketing, news — use SSG or SSR for SEO + speed.
- Sites needing instant first paint. SPAs are slow on cold load.
- Low-spec target devices. Old phones struggle with large JS bundles.
- Where SEO is critical. SPAs need SSR/prerendering, defeating some of the simplicity.
Solving SPA problems
SEO: SSR or prerendering
Server renders HTML for crawlers; SPA still hydrates for users. Next.js, Nuxt, SvelteKit, Remix do this. Or use a prerender service (Prerender.io) that serves cached HTML to bots.
Initial load: code splitting
// Lazy-load routes
const Settings = lazy(() => import('./Settings'));
const Reports = lazy(() => import('./Reports'));Browser navigation: use a router
// React Router
<Route path="/users/:id" element={<UserPage />} />
// Pushes to history; browser back worksLoading states
Skeleton screens or spinners while JS bundle downloads + renders. Critical for perceived performance.
SPA best practices
- Code-split aggressively. Lazy-load routes; only ship what's needed.
- Use a router. Don't roll your own; React Router, Vue Router are mature.
- Manage state explicitly. Redux, Pinia, Zustand for complex apps.
- Cache API responses. SWR, React Query, Apollo Client.
- Show loading states. Don't show blank screen during data fetches.
- Handle errors gracefully. Error boundaries; don't let one component crash everything.
- Optimize bundle size. Tree-shaking, dynamic imports, modern build tools.
- Add SSR/prerender if SEO matters. Don't ship pure SPA for marketing pages.
- Service Workers for offline. SPAs can work offline with PWA techniques.
Common SPA pitfalls
- Massive bundles. 500KB+ JS = slow on mobile. Code-split.
- SEO afterthought. Marketing pages built as SPA; Google can't index.
- Memory leaks. Long-running SPA accumulates state; reload needed.
- Browser history broken. Forgot to push to history; back button doesn't work.
- JS-only navigation. Hard for users on slow connections, bots.
- State management chaos. Prop-drilling everywhere; reach for Redux too late.
- Loading flicker. No skeleton; user sees blank then content.
FAQ: Single Page Application
SPA or MPA: which is better?
Depends. SPA for app-like dashboards; MPA (or SSG/SSR) for content sites. Modern frameworks blur the line.
Are SPAs bad for SEO?
By default, yes — empty HTML hurts crawlers. With SSR/prerendering, SPAs can be fully SEO-friendly. Modern Next.js/Nuxt/SvelteKit handle this.
Does Google index SPAs now?
Googlebot does run JavaScript, but it's slower and less reliable than crawling pre-rendered HTML. SSR is still recommended for SEO-critical SPAs.
What's a PWA vs SPA?
PWA (Progressive Web App) = SPA + offline support + installable. PWA is a superset; not all SPAs are PWAs.
How big should an SPA bundle be?
Aim for < 200KB initial JS (gzipped). Code-split routes; lazy-load heavy components.
Are SPAs dying with the rise of SSR/RSC?
No — for true app-like experiences (Gmail, Figma, Notion), SPAs are still ideal. Modern frameworks combine SPA + SSR for the best of both.
What's the difference between SPA and CSR?
CSR (Client-Side Rendering) is the rendering strategy. SPA is the architecture. Most SPAs use CSR but the terms aren't identical — an SSR site that hydrates to client navigation is also an SPA.
Load test SPA backend APIs with LoadFocus
SPAs hit your backend hard via XHR. LoadFocus runs JMeter and k6 scripts that simulate SPA traffic patterns from 25+ regions. Sign up free at loadfocus.com/signup.
Related LoadFocus Tools
Put this concept into practice with LoadFocus — the same platform that powers everything you just read about.