Dynamic Script Injection in Nuxt 3

useInjectScript Composable

Dynamically load 3rd party scripts once. In this example, We load EmailJS onClick.

useInjectScript.ts
export function useInjectScript({
  src,
  id,
  async = true,
}: {
  src: string
  id: string
  async?: boolean
}): Promise<boolean> {
  const scriptEl = document.getElementById(id)

  return new Promise((resolve, reject) => {
    if (id && scriptEl) {
      return resolve(true)
    }
    const script = document.createElement('script')
    script.src = src
    script.type = 'text/javascript'
    script.async = async
    script.id = id

    document.body.appendChild(script)

    script.onload = () => resolve(true)
    script.onerror = e => reject(e)
  })
}

Usage

component.vue
<script setup lang="ts">
  import { type Ref } from "vue";

  const emailJs: Ref<{
    init: (key: string) => void
    sendForm: (
      serviceId: string,
      templateId: string,
      form: HTMLElement | null,
      publicKey: string,
    ) => Promise<{ status: number, text: string }>
  } | null> = ref(null)

  // EmailJS
  async function initEmailJs() {
    const loaded = await useInjectScript({
      src: 'https://cdn.jsdelivr.net/npm/@emailjs/browser@3/dist/email.min.js',
      id: 'emailjs',
    })
    if (loaded && !emailJs.value) {
      emailJs.value = window?.emailjs
    }
  }
</script>

<template>
  <button @click="initEmailJs">
    Load EmailJS
  </button>
</template>