How to Load Third-Party Scripts in Nuxt.js

Written by Alexander Lichter

Introduction

Almost every modern web application is using some kind of dependency, even besides the preferred JavaScript framework.

While most of the dependencies are available through NPM, many of them also provide a link to a CDN (Content Delivery Network) version so it can be included in any project without additional efforts or build steps.

But there is also a fair amount of popular third-party libraries that don't provide an NPM package and rely fully on being implemented via the script tag. Common examples are newsletter integrations, contact forms, and the Stripe SDK to process payments.

Having only a CDN link brings some benefits like allowing seamless updates from the authors without having the developers to update their software but it also has it's drawbacks, especially when working with modern JavaScript frameworks.

The integration, especially when only needed on a single page, becomes more complex. In addition, developers have to deal with asynchronously loading scripts.

Let's see how we can add such third party dependencies in our Nuxt.js project!

Loading third party scripts globally in Nuxt.js

With Nuxt.js, loading third party scripts has been a breeze when they are needed everywhere. The only thing one would have to do is adding the corresponding link in the head object of the project's nuxt.config.js and vue-meta will do the heavy lifting.

// nuxt.config.js
export default {
  head: {
    title: 'My awesome project', // Other meta information
    script: [
      { hid: 'stripe', src: 'https://js.stripe.com/v3/', defer: true }
    ]
  }
}

This will defer the script but it will load it on every page, which is not suitable if you need it only certain pages like a payment page.

Adding Third party scripts to distinct pages

But thanks to Nuxt and vue-meta, you can also include the script inside page components which will load it only on the pages where you've added the script.

To accomplish that, the head function in the page components which works similar to the object in the nuxt.config.js is the key:

// /pages/payment-page.vue
export default {
  head() {
    return {
      title: 'Payment Page - My awesome project', // Other meta information
      script: [
        { hid: 'stripe', src: '', defer: true }
      ]
    }
  }
}

This looks quite similar to what we've done before, but it's not the final solution.

The problem

Learn Vue.js With Vue School

As explained in the introduction, developers have to deal with two states regarding a third-party script that is loaded in such a way: before it is loaded and when it is available.

When using Nuxt in SSR mode, the initial request to the page (where SSR kicks in) won't have the first state. If you navigate to the page containing a third party script after the first request though, the developers have to handle the additional state because even the mounted lifecycle hook might be executed before the script loaded.

Especially when your code depends on an API from a third party script, like it is the case when using Stripe, some issues were encountered in the past.

<template>
  <div>
    <!-- How do I know when Strip is ready? -->
    <SomeComponentDependingOnStripe/>
  </div>
</template>
<script>
// /pages/payment-page.vue
export default {
  head () {
    return {
      title: 'Payment Page - My awesome project', // Other meta information
      script: [
        { hid: 'stripe', src: '<https://js.stripe.com/v3/>', defer: true }
      ]
    }
  }
}
</script>

The solution: vue-meta to the rescue

Back then when the linked issues were raised, I'd have likely recommended a library like VueScript2 which handles async script loading on its own. But this would lead to more complexity and increased bundle size. The good news is that that vue-meta's maintainer pimlie introduced a
callback option in version 2.1 which allows developers to manipulate Vue attributes after the script has been loaded.

With this option we can ensure that our script-depending logic is executed after the script is fully available.

This also means that chaining third party scripts that depend on each other is also easier now.

So, let us adapt our example from above and add a callback:

    <template>
      <div>
        <SomeComponentDependingOnStripe v-if="isStripeLoaded"/>
      </div>
    </template>
    <script>
    // /pages/payment-page.vue
    export default {
      data () {
        return {
          isStripeLoaded: false
        }
      },
      head () {
        return {
          title: 'Payment Page - My awesome project',
          script: [
            {
              hid: 'stripe',
              src: '<https://js.stripe.com/v3/>',
              defer: true,
              // Changed after script load
              callback: () => { this.isStripeLoaded = true } 
            }
          ]
        }
      }
    }
    </script>

PS: vue-meta also introduced a skip option to allow loading these scripts even conditionally, e.g. when a payment modal pops up.

Conclusion

Loading third party scripts with Nuxt has always been easy when it's needed globally. With the help of vue-meta, handling depending logic before the third party dependency is available became a lot easier now as well.

Make sure you are using the latest version of vue-meta to enjoy the newly discovered features. If you are using Nuxt, vue-meta is included by default and should be already up to date. When you are using a Vue project, e.g. via vue-cli, make sure vue-meta is set to ^2.0.0 in your package.json and update your dependencies via npm update or yarn upgrade.

I hope you've learned a thing or two in the blog post. If that's the case, please make sure to spread to word and share the blog post with your colleagues!

Learn Vue.js with Vue School

Leave a Reply

Your email address will not be published. Required fields are marked *

Up Next:

Prepare for Vue.js 3

Prepare for Vue.js 3