Different Rendering, Different Everything
Single Page Application (SPA): the server sends a minimal HTML shell (<div id='root'></div>), JavaScript downloads and renders the entire UI in the browser. All navigation, data fetching, and rendering: happen client-side. Frameworks: React with Vite (no SSR), Vue with Vite, Angular. The browser does: all the work. The server serves: static files only. Data: fetched via API calls from the client (useEffect + fetch, TanStack Query, SWR).
Server-Side Rendering (SSR): the server renders the full HTML for each page request, sends it to the browser, then JavaScript hydrates the page for interactivity. Navigation: can be server-side (full page reload) or client-side (after hydration). Frameworks: Next.js, Nuxt, Remix, SvelteKit. The server does: the initial rendering and data fetching. The browser does: hydration and subsequent interactions. Data: fetched server-side (getServerSideProps, loader, async Server Component).
Without architecture rules: the AI generates useEffect data fetching in an SSR project (the server should fetch the data, not the client), server-side data loading in an SPA (no server to render the page), generateMetadata in an SPA (no server to inject meta tags), or client-side routing in a file-based SSR project (the server handles routing). The architecture determines: how data is fetched, how pages are rendered, how routing works, and how SEO is implemented.
Data Fetching: Client-Side vs Server-Side
SPA data fetching: client-side only. useEffect(() => { fetchUsers().then(setUsers); }, []) or better: const { data } = useQuery({ queryKey: ['users'], queryFn: fetchUsers }). The data: is fetched after the page loads (the user sees a loading spinner first). SEO crawlers: may not see the data (if they do not execute JavaScript). The pattern: render loading state → fetch data → render data. AI rule: 'SPA: TanStack Query for data fetching. No server-side data loading (no getServerSideProps, no loader, no async component). Loading states: skeleton or spinner while data fetches.'
SSR data fetching: server-side first. Next.js App Router: async function Page() { const users = await fetchUsers(); return <UserList users={users} />; }. The data: is fetched on the server before HTML is sent. The user sees: content immediately (no loading spinner for initial data). SEO crawlers: see the full content in the HTML. The pattern: server fetches → server renders HTML with data → browser receives complete page. AI rule: 'SSR: async Server Components for data. No useEffect for initial data (server handles it). Client components: only for interactivity, not data fetching.'
The data fetching rule is: the most impactful architecture rule. In an SSR project: the AI generating useEffect for initial data produces a waterfall (page loads, then data fetches, then re-renders — defeating the purpose of SSR). In an SPA: the AI generating server-side fetching produces code that cannot run (no server process to execute it). One rule about data fetching: aligns every data interaction with the correct architecture.
- SPA: TanStack Query / useEffect + fetch. Client-side only. Loading spinner first, then data
- SSR: async Server Components / loader functions. Server-side first. Content visible immediately
- SPA: no server-side data (no getServerSideProps, no loader). SSR: no useEffect for initial data
- SEO: SPA = crawlers may miss data (JS-dependent). SSR = crawlers see full content in HTML
- AI error: useEffect in SSR = defeats the purpose. Server fetch in SPA = no server to run it
SPA: the AI should generate TanStack Query for data (client-side). SSR: async Server Components (server-side). One data fetching rule aligns EVERY data interaction. useEffect in SSR = waterfall that defeats server rendering. Server fetch in SPA = no server to run it. The architecture determines the data pattern.
Routing: Client-Side vs File-Based
SPA routing: client-side with React Router (or Vue Router, Angular Router). <BrowserRouter><Routes><Route path='/' element={<Home />} /><Route path='/users/:id' element={<UserProfile />} /></Routes></BrowserRouter>. The browser: handles all navigation without server requests. URL changes: update the browser history and render the matching component. No server involvement: after the initial page load. AI rule: 'SPA: React Router for routing. BrowserRouter + Routes + Route. useNavigate for programmatic navigation. useParams for URL parameters.'
SSR routing: file-based (Next.js App Router, Nuxt, Remix). app/users/[id]/page.tsx maps to /users/:id. No router configuration needed: the file system IS the router. The server: matches the URL to the file, renders the page, and sends HTML. Client-side navigation: after hydration, Link components navigate without full page reloads. AI rule: 'SSR (Next.js): file-based routing in app/. page.tsx for route content. layout.tsx for persistent layout. Link component for navigation. No React Router.'
The routing rule prevents: the AI installing React Router in a Next.js project (Next.js has file-based routing — React Router is redundant and conflicts), creating app/users/[id]/page.tsx in a Vite SPA (no file-based routing — use React Router), or using useNavigate from React Router in a Next.js project (use next/navigation useRouter instead). The routing mechanism is: architecture-determined. SPA = explicit router config. SSR = file system routes.
- SPA: React Router (BrowserRouter, Routes, Route, useNavigate, useParams)
- SSR: file-based (app/users/[id]/page.tsx, layout.tsx, Link component, useRouter)
- SPA: no file-based routing. SSR: no React Router (file system IS the router)
- SPA: all navigation client-side after initial load. SSR: server matches URL to file
- AI error: React Router in Next.js = redundant conflict. File routes in Vite SPA = no effect
Next.js has file-based routing. Installing React Router: adds a second router that conflicts with the file system. The AI without a routing rule: may install React Router in Next.js (familiar from training data). One rule: 'SSR: file-based routing, no React Router' prevents: the most common architecture-confused dependency.
SEO: Client-Side Meta vs Server-Generated Meta
SPA SEO: meta tags set client-side with react-helmet or document.title. The HTML sent from the server: has generic meta tags (the same title/description for every page). Client-side JavaScript: updates the meta tags after rendering. SEO crawlers: may or may not see the updated meta tags (depends on JavaScript execution). For SPA SEO: consider pre-rendering (react-snap) or SSR for critical pages. AI rule: 'SPA: react-helmet-async for meta tags. SEO limitations: crawlers may not see client-rendered meta. Consider pre-rendering for SEO-critical pages.'
SSR SEO: meta tags generated server-side. Next.js: export const metadata = { title: '...', description: '...' } or export async function generateMetadata({ params }) for dynamic pages. The HTML sent from the server: has the correct meta tags for each page. SEO crawlers: see the full meta tags in the initial HTML (no JavaScript execution needed). SSR is: the SEO-friendly architecture by default. AI rule: 'SSR (Next.js): export metadata or generateMetadata for SEO. Title, description, og:image per page. Server-rendered meta = crawlers see everything.'
The SEO rule prevents: the AI generating export const metadata in an SPA (no server to process it — it is Next.js API), using react-helmet in a Next.js SSR project (Next.js has built-in metadata handling — react-helmet is redundant and may conflict), or omitting meta tags in an SSR project (SSR makes meta tags easy — not using them wastes the SEO advantage). The architecture determines: how meta tags are delivered to search engines.
When to Choose Each Architecture
Choose SPA when: the application is behind authentication (no SEO needed — dashboards, admin panels, internal tools), the application is highly interactive (real-time collaboration, complex state, canvas-based UIs), you want the simplest deployment (static files on a CDN, no server needed), or the application is: a desktop-like web app where initial load time is acceptable (the user waits for JS, then the app is fast). SPA is: the simpler architecture for non-SEO applications.
Choose SSR when: SEO matters (public pages that search engines must index — blogs, e-commerce, marketing), initial load performance matters (the user sees content before JavaScript loads), the application mixes public and authenticated pages (SSR for public, client components for authenticated), or you want the best Core Web Vitals scores (SSR produces: faster LCP, lower CLS, better INP). SSR is: the default for most web applications in 2026 (Next.js, Nuxt, Remix are SSR-first).
For most new projects in 2026: SSR (Next.js) is the default. The SSR framework handles: both server-rendered public pages AND client-side interactive features. An SSR project can: have SPA-like pages (fully client-rendered dashboards behind auth) within the same application. SPA is: increasingly a subset of what SSR frameworks provide, not a separate architecture. The AI rule: specifies which architecture the project uses and which patterns follow from that choice.
- SPA: behind auth, highly interactive, simple deployment, no SEO needed. Vite + React Router
- SSR: SEO matters, fast initial load, public + auth mixed, best Web Vitals. Next.js, Nuxt, Remix
- 2026 default: SSR (Next.js) for most projects. SPA-like pages within SSR for auth-gated sections
- SSR frameworks include SPA capability: you do not need a separate SPA for dashboards
- Architecture rule: one line in CLAUDE.md determines data fetching, routing, and SEO patterns
Next.js handles both: server-rendered public pages AND client-side interactive dashboards in one project. You do not need a separate SPA for auth-gated sections. SSR frameworks: include SPA capability. SPA: increasingly a subset of what SSR provides, not a separate architecture choice.
Architecture Rule Summary
Summary of SPA vs SSR AI rules.
- Data: SPA = TanStack Query client-side. SSR = async Server Components server-side
- Routing: SPA = React Router (explicit config). SSR = file-based (app/ directory IS the router)
- SEO: SPA = react-helmet (crawlers may miss). SSR = export metadata (crawlers see in HTML)
- Initial load: SPA = loading spinner then content. SSR = content visible immediately
- Hydration: SPA = no hydration (all client). SSR = server HTML + client hydration for interactivity
- 2026 default: SSR for most projects. SPA for auth-gated interactive apps without SEO
- One rule: 'SPA with Vite' or 'SSR with Next.js App Router' determines every pattern
- AI error: useEffect for data in SSR = waterfall. Server fetch in SPA = no server to run it