Creating a Nuxt Plugin: Get Past "Window Undefined" Issue With 3rd Party Libraries

Updated: 11/19/17 - Nuxt @ v1.0.0-rc11

When it comes to server-side rendering and static site generators, sometimes we want to use a 3rd party library that needs access to the Window. In terms of Nuxt, that can often cause a beginner to experience the dreaded "Window: undefined" error.

Other times we see a similar issue in Nuxt when trying to access the Window from within a life-cycle hook that is being rendered on the server-side. Quick tip: use the mounted life-cycle hook.

This post will focus on showcasing the injection of 3rd party libraries into the root of your Nuxt application. For you Angular developers, this is akin to adding a service to your module providers.

Main Issue

Nuxt is telling me that the Window is undefined when I'm trying to use a third party client side library.

Possible Solution

I checked here: https://nuxtjs.org/faq/window-document-undefined, I saw that I could do this:

    
        if (process.browser) {
            require("external_library")
        }
    

Okay - sure - that makes sense. Thanks to Nuxt internals, we're able to discern if we're in browser context if process.browser is true. That means I could turn this:

    
    // components/SomeVueComponent.vue
    import toastLibrary from "toastLibrary"

    export default {
        methods: {
            success () { // this does not work :(
                toastLibrary.successToast({
                    title: "Ohmygodpleasework",
                    message: "Savemenow"
                })
            },
            failure () { // Window is undefined :(
                toastLibrary.failureToast({
                    title: "Oops!",
                    message: "They don't hire me to write copy!"
                })
            }
        }
    }   
    

Into this:

    
        // components/SomeVueComponent.vue
        export default {
            methods: {
                success () {
                    if (process.browser) { // Works!
                        const toastLibrary = require("toastLibrary")
                        toastLibrary.successToast({
                            title: "Wait you're telling me",
                            message: "That I would need to do that at all times?"
                        })
                    }
                },
                failure () {
                    if (process.browser) { // Ugly, but it works!
                        const toastLibrary = require("toastLibrary")
                        toastLibrary.failureToast({
                            title: "This is not what...",
                            message: "DRY, IT'S NOT DRY"
                        })
                    }
                }
            }
        }
    

A Better Solution

With Nuxt there's a build convention that allows us to load Vue plugins on the client side and skip SSR, which is what is causing the 'Window undefined' issue. There is no Window in Server Side land.

While traversing the Nuxt documentation there are a few places that mention registering a Vue plugin, and to quote the docs "Some plugins might work only for the browser". This ends up being the perfect fix for the undefined Window issue, but the docs (currently) fail to mention that. (Pull request incoming!)

So, let's bring in our 3rd party library and inject it into the root of our Nuxt application.

Creating The Nuxt Plugin

The syntax here is very specific to Nuxt. Usually with Vue plugins you see some importing of libraries, some configuration, and then calling Vue.use(plugin), but the Nuxt team created a helper utility for us, check it out:

  1. Create the plugin file, bring in the library, and inject it:
    
        // plugins/toast-plugin.js
        import toast from 'toast-library'

        export default (ctx, inject) {
            inject('toast', toast) // registers as this.$toast
        }
    

Yeah - pretty simple, huh? In 1.0.0-rc7 the Nuxt team added this convention for us. Thanks team!

  1. Register the Vue plugin with Nuxt, and put it in the vendor list so it will only be included in your app bundle once:
    
        // nuxt.config.js
        module.exports = {
            vendor: [
                'toast-library'
            ]
        plugins: [
                {
                    src: '~/plugins/toast-plugin.js',
                    ssr: false
                }
            ]
        }
    

By setting ssr to false we're explicitly telling the plugin to only be included on the client side, where the Window is defined.

You're now ready to try the new syntax:

    
        // components/SomeVueComponent.vue
        export default {
            methods: {
                success () {
                    this.$toast.successToast({
                        title: "Oh thank heavens",
                        message: "It's working!!"
                    })
                },
                failure( ) {
                    this.$toast.failureToast({
                        title: "Oops!",
                        message: "At least it's more dry"
                    })
                }
            }
        }
    

Your Mileage May Vary

Depending on the library you are using, your implementation may have to look a little different. This worked great for Flickity and for Izitoast, both of which are solid libraries you should check out.