Currently Available: Need a skilled Software Developer for your next project?
Categories
Vue

Loading Third-Party Scripts in Vue Applications

Adding third-party scripts to Vue applications can be tricky. The usual approach of dropping a <script> tag into your HTML doesn't work well with modern Single Page Applications (SPAs). This is especially true when working with stacks like Laravel + Inertia + Vue, but the problem exists in any Vue application.

Let's look at how to properly load external scripts like analytics or CAPTCHAs in Vue applications.

The Challenge

In traditional websites, adding third-party scripts is straightforward:

<script src="https://external-service.com/script.js"></script>

However, in Vue applications (especially SPAs), this approach has several drawbacks:

  • Scripts might load multiple times
  • Loading timing can be unpredictable
  • Script initialization might fail if loaded too early/late
  • Hard to handle loading errors
  • Difficult to manage script lifecycle

The Solution: Composables

Vue composables provide an elegant solution for managing third-party scripts. Let's look at two common examples:

Example 1: Analytics Script

Here's how you might load an analytics script (like Plausible):

// useAnalytics.js
import { onMounted } from 'vue';

export function useAnalytics() {
    const loadScript = () => {
        return new Promise((resolve, reject) => {
            if (window.analytics) {
                resolve();
                return;
            }

            const script = document.createElement('script');
            script.defer = true;
            script.src = 'https://analytics-service.com/script.js';
            script.onload = () => resolve();
            script.onerror = () => reject(new Error('Failed to load analytics'));
            document.head.appendChild(script);
        });
    };

    onMounted(() => {
        loadScript().catch(console.error);
    });

    return {
        trackEvent: (name, data) => {
            if (window.analytics) {
                window.analytics(name, data);
            }
        }
    };
}

Example 2: reCAPTCHA Integration

A more complex example with initialization requirements:

// useRecaptcha.js
import { ref, onMounted } from 'vue';

export function useRecaptcha(siteKey) {
    const isLoaded = ref(false);

    const loadScript = () => {
        return new Promise((resolve, reject) => {
            if (window.grecaptcha) {
                resolve();
                return;
            }

            const script = document.createElement('script');
            script.src = `https://www.google.com/recaptcha/api.js?render=${siteKey}`;
            script.onload = () => resolve();
            script.onerror = () => reject(new Error('Failed to load reCAPTCHA'));
            document.head.appendChild(script);
        });
    };

    const execute = async (action) => {
        if (!window.grecaptcha) return null;

        try {
            return await window.grecaptcha.execute(siteKey, { action });
        } catch (error) {
            console.error('reCAPTCHA execution failed:', error);
            return null;
        }
    };

    onMounted(async () => {
        try {
            await loadScript();
            isLoaded.value = true;
        } catch (error) {
            console.error(error);
        }
    });

    return {
        isLoaded,
        execute
    };
}

Using the Composables

In your components:

<script setup>
import { useAnalytics } from '@/composables/useAnalytics';
import { useRecaptcha } from '@/composables/useRecaptcha';

const { trackEvent } = useAnalytics();
const { execute } = useRecaptcha('your-site-key');

const handleSubmit = async () => {
    const token = await execute('submit');
    if (token) {
        // Process form...
        trackEvent('form_submitted', { success: true });
    }
};
</script>

Benefits of This Approach

  1. Script Loading Control: Scripts load only when needed and only once
  2. Error Handling: Proper error handling for script loading failures
  3. Type Safety: Better TypeScript support compared to global scripts
  4. Testing: Easier to mock for testing
  5. Reusability: Composables can be shared across components
  6. Lifecycle Management: Scripts are properly initialized with component lifecycle

Common Pitfalls to Avoid

  • Don't use <script> tags in component templates - they won't work as expected in SPAs
  • Don't assume scripts are immediately available after loading - always check for global objects
  • Don't forget error handling - external services can fail
  • Do consider using a loading state to handle script initialization
  • Do implement proper cleanup in onUnmounted if needed
What I'm building

Delegate tasks. Get software.

Give Vroni a GitHub issue, bug report, spec, or rough idea. It reads the repo, plans the change, writes code, runs checks, and works toward a review-ready pull request.

Take a look at vroni.com

Subscribe to my newsletter

Get new posts when I publish them.

I respect your privacy. Unsubscribe at any time.

Leave a Reply

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