Getting Your Quasar App SSR‑Ready: A Practical Guide
Learn how to turn a Quasar Vue 3 project into a fast, SEO‑friendly server‑side rendered app. From CLI flags to asyncData hooks, see a concrete example, trade‑offs, and production hints.
17 Sept 2025, 02:17 UTC

Why SSR in Quasar?
Server‑Side Rendering (SSR) turns a Vue 3 app into a pre‑rendered HTML page that the browser receives first. That means the initial paint is faster and search engines can crawl content that would otherwise be hidden behind JavaScript. For a Quasar project, SSR is a single command away: quasar create my-app --ssr. The CLI scaffolds a quasar.conf.js with an ssr block and an Express server that can be deployed to any Node runtime.
Quick Setup Checklist
- Node 14+ installed (SSR requires ES modules and newer Node features).
- Quasar CLI 2.x or newer.
- Run
quasar dev --ssrto start the dev server. - Run
quasar build -m ssrto producedist/ssr/server.js. - Guard browser‑only APIs with
if (process.client)ordefineComponent({ /* ... */ })checks. - Use
asyncDataor Vuex actions that return a promise for data needed on the first render.
Configuring Data Fetching with asyncData
Quasar’s SSR engine waits for any promise returned from asyncData or a Vuex action before sending the HTML. This guarantees that the markup contains the fetched data.
Example: a component that lists posts from JSONPlaceholder.
// src/pages/PostsPage.vue
Posts
- {{ post.title }}
In the store:
// src/store/posts.js
export default {
namespaced: true,
state: () => ({ items: [] }),
actions: {
async fetchAll({ commit }) {
const res = await fetch('https://jsonplaceholder.typicode.com/posts')
const data = await res.json()
commit('setItems', data)
}
},
mutations: {
setItems(state, payload) {
state.items = payload
}
}
}
When you run quasar dev --ssr, the server will wait for fetchAll to resolve, inject the list into the initial HTML, and then hydrate on the client. If you skip asyncData and rely on mounted to fetch data, the first paint will show an empty list, defeating SSR’s purpose.
Guarding Browser‑Only Code
Any reference to window, document, or other browser globals will crash the Node server. Wrap such code with if (process.client) or use the defineComponent clientOnly option.
// src/components/Chart.vue
Production‑Ready SSR Server
The default Express server in dist/ssr/server.js is fine for low‑traffic sites, but production deployments often need clustering or a process manager. PM2 is a common choice:
# Install PM2 globally
npm install -g pm2
# Start the SSR server with clustering
pm2 start dist/ssr/server.js -i max
PM2 will spawn one worker per CPU core, automatically restarting crashed workers and providing load‑balancing. For larger workloads, consider a reverse proxy (NGINX or Caddy) in front of PM2.
SSR + PWA: Avoid Duplicate Service Workers
Quasar lets you combine SSR with PWA by adding pwa: true to the ssr block. When you do this, the build generates a service-worker.js that must only run on the client. If you forget to guard its registration, the server will try to register the service worker and throw a ReferenceError. Make sure the registration code lives inside if (process.client) or inside a client-only component.
Trade‑offs and Limitations
| Aspect | SSR | SPA |
|---|---|---|
| First‑paint speed | Fast (pre‑rendered) | Slower (client render) |
| SEO friendliness | High | Low (requires pre‑rendering) |
| Server load | Higher (Node server per request) | Minimal |
| Deployment complexity | Requires Node runtime or container | Static files only |
| Dynamic client‑only code | Must be guarded | No restrictions |
Actionable Checklist Before Going Live
- Run
quasar dev --ssrand verify the page source contains the expected HTML. - Check for hydration warnings in the browser console; if present, ensure Vuex state is re‑hydrated correctly.
- Build with
quasar build -m ssrand start the server locally withnode dist/ssr/server.jsto confirm it serves HTML without a browser. - Deploy the
dist/ssrfolder to your Node host or container. Use PM2 or clustering for production. - If using PWA, confirm that the service worker is registered only on the client side and that your
manifest.jsonis correct. - Run a lightweight load test (e.g.,
ab -n 1000 -c 10 http://yourhost/) to ensure the server can handle concurrent requests. - Monitor logs for any
ReferenceErrororHydration mismatchwarnings and adjust guards accordingly.
With these steps, your Quasar app will deliver fast, SEO‑friendly pages without sacrificing the rich interactivity that Vue 3 and Quasar provide. Happy coding!
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.