SPA Architecture
In client-side MVC (also called the Single-Page Application or SPA pattern), the server acts purely as a data API. It sends JSON, and the browser's JavaScript takes full responsibility for rendering the user interface.
Client-Side MVC (SPA) Flow:
+---------------------------+ +--------------------------+
| Browser | | Server |
| | | |
| +---------------------+ | fetch() / XHR | +--------------------+ |
| | JavaScript App | | -----------------> | | Controller | |
| | | | | | | |
| | +---------------+ | | | | Receives request | |
| | | View (React, | | | | | Calls Model | |
| | | Vue, Angular) | | | | +--------+-----------+ |
| | | | | | | | |
| | | Renders DOM | | | JSON | +--------v-----------+ |
| | | in browser | | | <----------------- | | Model | |
| | +---------------+ | | | | | |
| +---------------------+ | | | Queries DB | |
| | | | Returns data | |
+---------------------------+ | +--------------------+ |
| |
| Database <--> |
+--------------------------+
Step-by-Step Flow
- Browser loads a minimal HTML shell — the initial page is mostly empty, with a
<script>tag that loads the JavaScript application bundle - JavaScript app initializes — the framework (React, Vue, Angular) boots up and takes control of the page
- App makes API request — the JavaScript calls
fetch('/api/users')to request data from the server - Server Controller processes request — the server receives the request, calls the Model, and returns JSON data
- Model queries database — the model fetches data and returns it; the controller sends it as a JSON response
- JavaScript renders the DOM — the client-side View receives the JSON and builds/updates the HTML in the browser using DOM manipulation
Key characteristic: The server returns JSON data only. The client-side JavaScript is responsible for all rendering. Navigation between "pages" happens entirely in the browser without full page reloads.
Client-Side MVC Components
In a SPA, the MVC roles shift to the browser. Here is how each component maps to client-side JavaScript:
Model: Data Fetching with fetch()
The client-side Model manages data by making HTTP requests to the server's JSON API. It has no knowledge of the DOM or how data is displayed:
View: DOM Manipulation
The client-side View builds and updates HTML elements in the browser. It receives data and produces DOM nodes, but does not fetch data or handle events:
Controller: Event Handling
The client-side Controller listens for user events (clicks, form submissions), calls the Model to get or save data, and passes the result to the View:
XSS Safety: textContent vs innerHTML
In server-side MVC, template engines handle HTML escaping automatically. In client-side MVC, you are responsible for preventing XSS. The critical distinction is between textContent and innerHTML:
Server-Side vs Client-Side MVC
| Aspect | Server-Side MVC | Client-Side MVC (SPA) |
|---|---|---|
| Rendering | Server renders HTML | Browser renders via JavaScript |
| Data format | Complete HTML pages | JSON from API |
| Navigation | Full page reload | In-browser, no reload |
| Initial load | Fast (HTML ready immediately) | Slower (must load JS bundle first) |
| SEO | Excellent (HTML is crawlable) | Poor (requires SSR workaround) |
| JavaScript required | No | Yes |
| Interactivity | Limited (requires page reload) | Rich (instant updates) |
| XSS prevention | Template engine escaping | textContent (manual) |
When to Use Client-Side MVC
- Highly interactive UIs — real-time dashboards, collaborative editors, chat applications
- Desktop-like experiences — apps where constant page reloads would feel jarring (e.g., email clients, project management tools)
- Offline-capable applications — SPAs can cache data locally and sync when reconnected
- Separate frontend/backend teams — the API contract (REST endpoints) is the only shared interface
Client-Side Frameworks
| Framework | Approach |
|---|---|
| React | Component-based, virtual DOM |
| Vue | Reactive data binding, single-file components |
| Angular | Full framework with dependency injection, RxJS |