Use more client-side libs with Nuxt SSR
Server-side rendering is a great way to make your NuxtJS site more SEO-friendly, as it sends a fully rendered page to the client. However, this doesn't work with libraries that require access to DOM objects like document
and window
. These can only be used on the client. To ensure that such code is not executed on the server, you can place it in the beforeMount
or mounted
hooks, which are only triggered on the client. Unfortunately, this is not sufficient for some libraries.
One such example is the JavaScript checkout library for the Payvision payment gateway. If you follow the advice above, placing the sample code from the official library documentation into the mounted
hook of a NuxtJS page should suffice:
<template>
<div>
<div>Header</div>
<div id="checkout-container"></div>
</div>
</template>
import Vue from "vue";
import Checkout from "@payvision/checkout-library";
export default Vue.extend({
mounted: () => {
const options = {
live: false,
};
const checkout = new Checkout(
"{{CHECKOUT_ID}}",
"checkout-container",
options
);
checkout.render();
},
});
This code works fine when server-side rendering is disabled. However, as soon as you enable it, it fails with the following error:
ReferenceError: window is not defined
Of course, the window
object is not defined on the server. But why is the code executed on the server if it is placed in the mounted
hook?
The code in the mounted
hook is not executed at all.You can try commenting out everything in it, and the page will still fail with the same error. The offending code accesses the window
object when the Checkout
object is imported:
import Checkout from "@payvision/checkout-library";
To fix the error, don't import this object on the server in the first place. How? By importing it dynamically at the top of the mounted
hook instead:
import Vue from "vue";
export default Vue.extend({
mounted: () => {
const Checkout = require("@payvision/checkout-library");
const options = {
live: false,
};
const checkout = new Checkout(
"{{CHECKOUT_ID}}",
"checkout-container",
options
);
checkout.render();
},
});
With this change, the code works even if server-side rendering is enabled, since the Checkout
object is no longer imported on the server.
You can get a NuxtJS sample project with the code from this post in my GitHub repository. The last commit contains the final working code. The one before that contains the code that only works with server-side rendering disabled and fails on the server.
If you have NextJS server-side rendering enabled, keep in mind that not all JavaScript code will work on the server because there is no DOM available there. Usually it helps to put the offending code in the beforeMount
or mounted
hook. But some libraries run their client-side code as soon as you import them into a file. In these cases, it helps to import them in a client-side hook using the require
function.