Preventing Flash Messages from Reappearing on Browser Back Navigation in Laravel + Vue/Inertia apps
If you're using flash messages in your Laravel + Vue/Inertia application (or any SPA really), you might have encountered this annoying issue:…
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.
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:
Vue composables provide an elegant solution for managing third-party scripts. Let's look at two common examples:
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);
}
}
};
}
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
};
}
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>
<script> tags in component templates - they won't work as expected in SPAsonUnmounted if neededGive 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