Why donate
API Explorer
Upgrade guide
NEW!
The quasar.config file
Convert to CLI with Webpack
Browser Compatibility
Supporting TypeScript
Directory Structure
Commands List
CSS Preprocessors
Routing
Lazy Loading - Code Splitting
Handling Assets
Boot Files
Prefetch Feature
API Proxying
Handling Webpack
Handling process.env
State Management with Pinia
State Management with Vuex
Linter
Testing & Auditing
Developing Mobile Apps
Ajax Requests
Opening Dev Server To Public
Quasar CLI with Webpack - @quasar/app-webpack
Configuring SSR

quasar.config file

This is the place where you can configure some SSR options. Like if you want the client side to takeover as a SPA (Single Page Application – the default behaviour), or as a PWA (Progressive Web App).

/quasar.config file

return {
  // ...
  ssr: {
    pwa: true/false, // should a PWA take over (default: false), or just a SPA?

    /**
     * Manually serialize the store state and provide it yourself
     * as window.__INITIAL_STATE__ to the client-side (through a <script> tag)
     * (Requires @quasar/app-webpack v3.5+)
     */
    manualStoreSerialization: false,

    /**
     * Manually inject the store state into ssrContext.state
     * (Requires @quasar/app-webpack v3.5+)
     */
    manualStoreSsrContextInjection: false,

    /**
     * Manually handle the store hydration instead of letting Quasar CLI do it.
     * For Pinia: store.state.value = window.__INITIAL_STATE__
     * For Vuex: store.replaceState(window.__INITIAL_STATE__)
     */
    manualStoreHydration: false,

    /**
     * Manually call $q.onSSRHydrated() instead of letting Quasar CLI do it.
     * This announces that client-side code should takeover.
     */
    manualPostHydrationTrigger: false,

    prodPort: 3000, // The default port that the production server should use
                    // (gets superseded if process∙env∙PORT is specified at runtime)

    maxAge: 1000 * 60 * 60 * 24 * 30,
        // Tell browser when a file from the server should expire from cache
        // (the default value, in ms)
        // Has effect only when server.static() is used

    // List of SSR middleware files (src-ssr/middlewares/*). Order is important.
    middlewares: [
      // ...
      'render' // Should not be missing, and should be last in the list.
    ],

    // optional; add/remove/change properties
    // of production generated package.json
    extendPackageJson (pkg) {
      // directly change props of pkg;
      // no need to return anything
    },

    // optional;
    // handles the Webserver webpack config ONLY
    // which includes the SSR middleware
    extendWebpackWebserver (cfg) {
      // directly change props of cfg;
      // no need to return anything
    },

    // optional; EQUIVALENT to extendWebpack() but uses webpack-chain;
    // handles the Webserver webpack config ONLY
    // which includes the SSR middleware
    chainWebpackWebserver (chain) {
      // chain is a webpack-chain instance
      // of the Webpack configuration
    }
  }
}

If you decide to go with a PWA client takeover (which is a killer combo), the Quasar CLI PWA mode will be installed too. You may want to check out the Quasar PWA guide too. But most importantly, make sure you read SSR with PWA page.

When building, extendWebpack() and chainWebpack() will receive one more parameter (Object), currently containing isServer or isClient boolean props, because there will be two Webpack builds (one for the server-side and one for the client-side).

/quasar.config file

build: {
  extendWebpack(cfg, { isServer, isClient }) { ... }
}

If you want more information, please see this page that goes into more detail about handling webpack in the /quasar.config file.

Manually triggering store hydration

By default, Quasar CLI takes care of hydrating the Vuex store (if you use it) on client-side.

However, should you wish to manually hydrate it yourself, you need to set quasar.config file > ssr > manualStoreHydration: true. One good example is doing it from a boot file:

Some boot file

// MAKE SURE TO CONFIGURE THIS BOOT FILE
// TO RUN ONLY ON CLIENT-SIDE

export default ({ store }) => {
  // For Pinia
  store.state.value = window.__INITIAL_STATE__

  // For Vuex
  store.replaceState(window.__INITIAL_STATE__)
}

Manually triggering post-hydration

By default, Quasar CLI wraps your App component and calls $q.onSSRHydrated() on the client-side when this wrapper component gets mounted. This is the moment that the client-side takes over. You don’t need to configure anything for this to happen.

However should you wish to override the moment when this happens, you need to set quasar.config file > ssr > manualPostHydrationTrigger: true. For whatever your reason is (very custom use-case), this is an example of manually triggering the post hydration:


// App.vue

import { onMounted } from 'vue'
import { useQuasar } from 'quasar'
export default {
  // ....
  setup () {
    // ...
    const $q = useQuasar()
    onMounted(() => {
      $q.onSSRHydrated()
    })
  }
}

Nodejs Server

Adding SSR mode to a Quasar project means a new folder will be created: /src-ssr, which contains SSR specific files:

middlewares/
# SSR middleware files
directives/
# SSR transformations for Vue directives
production-export.js
# SSR webserver production export

You can freely edit these files. Each of the two folders are detailed in their own doc pages (check left-side menu).

Notice a few things:

  1. If you import anything from node_modules, then make sure that the package is specified in package.json > “dependencies” and NOT in “devDependencies”.

  2. The /src-ssr/middlewares is built through a separate Webpack config. You will see this marked as “Webserver” when Quasar App CLI builds your app. You can chain/extend the Webpack configuration of these files through the /quasar.config file:

/quasar.config file

return {
  // ...
  ssr: {
    // ...

    // optional; webpack config Object for
    // the Webserver part ONLY (/src-ssr/)
    // which is invoked for production (NOT for dev)
    extendWebpackWebserver (cfg) {
      // directly change props of cfg;
      // no need to return anything
    },

    // optional; EQUIVALENT to extendWebpack() but uses webpack-chain;
    // the Webserver part ONLY (/src-ssr/)
    // which is invoked for production (NOT for dev)
    chainWebpackWebserver (chain) {
      // chain is a webpack-chain instance
      // of the Webpack configuration
    }
  }
}
  1. The /src-ssr/production-export.js file is detailed in SSR Production Export page. Read it especially if you need to support serverless functions.

Helping SEO

One of the main reasons when you develop a SSR instead of a SPA is for taking care of the SEO. And SEO can be greatly improved by using the Quasar Meta Plugin to manage dynamic html markup required by the search engines.

Boot Files

When running on SSR mode, your application code needs to be isomorphic or “universal”, which means that it must run both on a Node context and in the browser. This applies to your Boot Files too.

However, there are cases where you only want some boot files to run only on the server or only on the client-side. You can achieve that by specifying:

/quasar.config file

return {
  // ...
  boot: [
    'some-boot-file', // runs on both server and client
    { path: 'some-other', server: false }, // this boot file gets embedded only on client-side
    { path: 'third', client: false } // this boot file gets embedded only on server-side
  ]
}

Just make sure that your app is consistent, though.

When a boot file runs on the server, you will have access to one more parameter (called ssrContext) on the default exported function:

Some boot file

export default ({ app, ..., ssrContext }) => {
  // You can add props to the ssrContext then use them in the src/index.template.html.
  // Example - let's say we ssrContext.someProp = 'some value', then in index template we can reference it:
  // {{ someProp }}
}

When you add such references (someProp surrounded by brackets in the example above) into your src/index.template.html, make sure you tell Quasar it’s only valid for SSR builds:

/src/index.template.html

<% if (ctx.mode.ssr) { %>{{ someProp }} <% } %>
2.2. Manually triggering post-hydration