Getting SSR to work with Quasar Framework with Vue Composition as the State Management Choice

Coding Friend, LLC
6 min readJan 8, 2021

Hello readers,

With the release of the Vue Composition API and Vue 3 we now have some amazing methods that will make our lives so much easier. Namely, reactive({}) and computed(() => {}).

I have been using reactive and computed methods for my state management system in Vue for the past 1–2 years in personal and company apps and it has worked great. I create a file where I create a global store (so I can access the state through the console) and then make sure to include that store in my returned properties in the `setup` method of all the Vue components that need access. Alternately, a better method may be to use the provide/inject functionality of Vue as has been documented in several trending articles lately.

My favorite framework in Vue is Quasar Framework but currently it doesn’t support Vue Composition API as your State Management choice if you wish to get prefetch and SSR working in your components.

https://quasar.dev/quasar-cli/prefetch-feature

Prefetch is important if you need to make an API call before displaying the page to fetch data that should be visible to SEO bots.

After a few hours of research and digging into Quasar’s source code, I discovered a way you can get this to work with minimal effort. Granted, I would probably consider this a hack and you should generally be wary of hacks. When you use a framework or library in a way the designer did not intend it to be used, the designer might write code/updates in the future that break your hacks. An additional concern is such hacks have a greater risk of incurring bugs and perhaps of the kind that you can’t find solutions for on StackOverflow.

However, since many programmers are interested in using reactive as their state management choice, Quasar will likely support this in the future without the need for my hack allowing you to replace this code. In the meantime, this is a simple hack that has worked well in our project.

Let’s start with an overview of how SSR works, prefetch, and client hydration.

Why Server Side Rendering?

One of Vue’s selling features is that it’s super fast and uses AJAX technologies to replace data on the page without needing a full page refresh after the page has loaded. This means your scripts, images, CSS and page structure don’t have to update every time you navigate within the site and instead only the data that is expected to change updates.

Unfortunately, Google SEO bots only take their snapshot of the SEO of the page the moment the page finishes loading, but since Vue often doesn’t start fetching data until after the page is loaded, good SEO strategies fail. To fix the lack of good SEO on Vue.js sites, Server Side Rendering was invented. SSR allows you to render a Vue app as a string on a server before it’s ever sent to the client. When the client receives the string, it displays it as html before on the client before the page has finished loading. This allows SEO bots to take a snapshot of the page with all the necessary information.

What is Prefetch?

Often pages need external data to display properly. When you click on a product on Amazon you expect to see prices, descriptions, reviews, and images belonging specifically to that product. So do SEO bots. But since that information is likely saved in your database, you have to make an API/AJAX call to fetch it and then include it in your template. prefetch allows you to fetch data necessary for the proper display and SEO of a page from your server before the page is finished loading so bots can index it.

What is client side hydration?

After the client receives the rendered html string from the server with all the necessary data included and displaying it, it attempts to rebuild the page client side so the javascript is functional. (This allows you to click buttons, navigate and such). But this leads to our first hurdle. When the client renders, it needs the same data that SSR already fetched on the server before it was sent to the client. We don’t really want to fetch all that information a second time because it puts twice the load on our server and database. So instead the server includes a global variable containing the prefetched data for the page embedded in the html string sent to the client that the client can take and use without having the fetch the data itself a second time.

Vue saves this prefetched data in window.__INITIAL_STATE__. It is the responsibility of the client to take the data in this variable and merge it with the state management store right after the page loads.

So here’s the hack

In Quasar, the prefetch option is tightly integrated with the Vuex store. In order for prefetch to be able to modify the state of our global store on the server and client, Quasar expects a file named src/store/index.js to exist. The trick is to make our reactive store to look like a Vuex store in this file. This file returns a function that returns an object that must contain a state property and replaceState method. Whatever state here equals will be what the server saves to window.__INITIAL_STATE__ when it loads the client, you’ll want to make the state property reference (not clone) your reactive global store.

The replaceState is necessary for client side hydration. It should take whatever value is retrieved in window.__INITIAL_STATE__ and merge those properties with those of your reactive store object. I recommend looping through the initial_states’ properties and assigning them to the client’s store one by one. It’s also important that you don’t try to assign a computed property that may be in your initial state with the store on the client or else it will complain. I like to save all my computed properties in a sub-object in the store under the property compute so I can easily weed them out

Something like this should suffice:

This assumes you have a file somewhere that looks like this:

import { reactive, computed } from '@vue/composition-api'/*** Data that will be shared between multiple pages should be added here* We make this store global so we can access it for inspection in the console*/global.store = reactive({  current_page: 'home',  drawerOpen: false,  computed: {
filtered_results: computed(() => { ... })
}, ...
})
export default store

To make it work with prefetch in your page component you have to leave out the store argument.

<script>
export default {
preFetch ({
// store, //don't include this argument
currentRoute,
previousRoute,
redirect,
ssrContext,
urlPath,
publicPath
}) {
return fetch_product(currentRoute.params, currentRoute.fullPath) .catch(err => { console.log(`Fetching Product Err`, err) redirect(previousRoute.path) }) }
}
</script>

Here is the Quasar Source Code that warrants this hack:

Quasar here is looking for a default export in src/store/index.js.

https://github.com/quasarframework/quasar/blob/684ed9fbe5320552d69ee32393a305205894fd8c/app/templates/entry/app.js

Here Quasar calls this exported store method as it is performing Server Side Rendering and saves it in a variable named store.

https://github.com/quasarframework/quasar/blob/6fe7c10e6d48fafd45ec9cb750d193208664995b/app/templates/entry/server-entry.js

Whatever is stored in context.state will be added to window.__INITIAL_STATE__ before being sent to the client. Since Quasar is taking that store variable it fetched earlier and including whatever data that’s on its state property in context.state that data effectively gets saved to window.__INITIAL_STATE__. That’s why we make it reference our reactive store.

https://github.com/quasarframework/quasar/blob/4e4b34d9471ef19e4d508edd241c62c148da5606/app/templates/entry/client-entry.js

Finally, unless you’ve enabled manualHydration in the quasar.conf.js, Quasar will automatically call the replaceState method of the store variable passing in window.__INITIAL_STATE__.

You should write the replaceState method to accept this argument and use it’s information to modify your reactive client store however you like or as I show in the screenshot.

Remember, Prefetch only works in pages not components.

There you go. A minimal hack to get your Vue Composition API reactive store working in Quasar with SSR Prefetch.

--

--