How to Configure SeaText for a Vue.js Single-Page Application
Add the SeaText snippet to your Vue app's entry point, then use Vue Router navigation guards to trigger re-translation on every route change. This ensures dynamic content and client-side routing work correctly with SeaText's...
To configure SeaText for a Vue.js SPA, paste the provided JavaScript snippet into your index.html (or the main bootstrap file) so it loads once when the app starts. Then hook into Vue Router's afterEach guard to call window.seatext?.retranslate() (or the equivalent API method) after every navigation. This two-step setup — one-time snippet load plus router-guard re-translation — handles client-side routing, lazy-loaded components, and any DOM mutations that happen after the initial render.
Prerequisites before you start
- A working Vue 2 or Vue 3 project with Vue Router installed (v3 or v4).
- Access to the SeaText dashboard to copy your unique integration snippet (the
SEATEXTCODEINTEGRATIONblock). - Permission to edit
index.htmlor the main entry file (main.js,main.ts,app.js). - Local storage enabled in the browser — SeaText stores an anonymous visitor ID there.
Step 1 — Add the snippet at the app entry point
- Open
public/index.html(Vue CLI / Vite default) or the equivalent template file. - Paste the SeaText snippet inside the
<body>tag, preferably just before the closing</body>so it doesn't block rendering. The snippet loads asynchronously thanks to theasyncattribute, so page-load performance stays intact. - Save and rebuild (
npm run buildornpm run serve).
Why the entry point? SeaText must initialise once per session. In an SPA the HTML shell loads only once; subsequent "pages" are virtual. Placing the snippet in index.html guarantees it runs before any route components mount.
Step 2 — Listen to Vue Router navigation guards
After the initial load, SeaText has no idea when the virtual URL changes. You must tell it. The pattern is identical for Vue Router 3 and 4; only the import path differs.
Vue Router 4 (Vue 3 default)
// src/router/index.js or .ts
import { createRouter, createWebHistory } from 'vue-router'
const router = createRouter({ history: createWebHistory(), routes: [...] })
router.afterEach((to, from) => {
// Give Vue a tick to finish DOM updates
nextTick(() => {
if (window.seatext && typeof window.seatext.retranslate === 'function') {
window.seatext.retranslate()
}
})
})
export default router
Vue Router 3 (Vue 2)
// src/router.js
import Vue from 'vue'
import Router from 'vue-router'
Vue.use(Router)
const router = new Router({ mode: 'history', routes: [...] })
router.afterEach((to, from, next) => {
Vue.nextTick(() => {
if (window.seatext && typeof window.seatext.retranslate === 'function') {
window.seatext.retranslate()
}
next()
})
})
export default router
nextTick (or Vue.nextTick) ensures the new route's components have rendered before SeaText scans the DOM for translatable nodes.
Step 3 — Handle dynamic content that arrives after route load
Some Vue apps fetch data or render heavy components after the route hook fires (e.g., Suspense, async components, infinite scroll). Two practical options:
- Call retranslate manually inside the component's
onMountedor after the async data resolves:window.seatext?.retranslate(). - Use a global event bus (mitt, Vue 3's
emits, or a simplewindow.dispatchEvent) so any component can notify SeaText when new markup lands.
Pick the approach that matches your codebase — both keep SeaText in sync without polling.
Step 4 — Configure AI scope and selectors (optional but recommended)
In the SeaText dashboard under How to configure / AI scope you can limit which DOM nodes the AI touches. For a Vue SPA, add CSS selectors that match your main content containers (e.g., #app > .page-content, .vue-component-root). This prevents the AI from rewriting navigation bars, footers, or third-party widgets that should stay static.
Step 5 — Verify the integration end-to-end
- Run the dev server (
npm run devornpm run serve). - Open Chrome DevTools → Console and Network tabs. Confirm the SeaText script loads with HTTP 200 and no CORS errors.
- Navigate between several routes. Watch the Console for
SeaText: retranslate triggered(or similar log) after each navigation. - Switch the SeaText dashboard language selector to a target language. The visible text on each route should update without a full page reload.
- Check
localStoragein Application tab — aseatext_visitor_idkey should exist.
If any step fails, see the Common pitfalls section below.
Common pitfalls and how to fix them
| Symptom | Likely cause | Fix |
|---|---|---|
| Translations work on first load but not after route change | Missing or mis-typed router.afterEach hook | Add the guard exactly as shown in Step 2; ensure nextTick wraps the call. |
Console shows seatext is not defined | Snippet not loaded or blocked by CSP | Verify snippet in index.html; check Content-Security-Policy allows script-src from SeaText domain. |
| Only part of the page translates | AI scope selectors too narrow or Vue component root not matched | Adjust selectors in dashboard to include the dynamic wrapper (e.g., .router-view-wrapper). |
| Cross-origin errors in multi-domain SPA | SeaText script served from different origin than API calls | Host the snippet on your CDN or configure Access-Control-Allow-Origin headers per SeaText docs. |
| Local storage quota exceeded | Other scripts filling storage | Clear storage in dev tools; SeaText only stores a tiny ID string. |
Key facts at a glance
| Item | Detail |
|---|---|
| Snippet loading | Async script tag; non-blocking |
| Initialisation point | index.html or main bootstrap file |
| Router integration | router.afterEach + nextTick + window.seatext.retranslate() |
| Dynamic content handling | Manual retranslate() call or global event bus |
| AI scope control | Dashboard CSS selectors |
| Storage requirement | Local storage for anonymous visitor ID |
| Cross-origin note | Ensure snippet domain allowed in CSP / CORS |
| Verification steps | DevTools Console/Network, language switch test, localStorage check |
Limitations and when this guide does not apply
- Nuxt.js, Quasar, or other meta-frameworks — they have their own plugin/entry systems; adapt the snippet placement accordingly.
- Server-side rendered (SSR) pages — SeaText runs in the browser only; SSR HTML will not be translated until hydration.
- Apps that disable local storage or run in strict privacy modes (e.g., Safari ITP) — SeaText may fall back to session storage or cookie, but behaviour can differ.
- Non-Vue SPAs (React, Angular, Svelte) — the router guard concept is the same, but the API differs; see SeaText docs for framework-specific examples.
Frequently asked questions
Do I need to re-initialise SeaText on every route?
No. The snippet initialises once. You only need to call retranslate() (or the current API equivalent) after the DOM updates.
Can I use SeaText with Vue 2 Options API?
Yes. The router guard lives in the router file, independent of component API style. Call this.$nextTick(() => window.seatext?.retranslate()) inside a component if you prefer component-level control.
What if my app uses hash mode (#/route) instead of history mode?
The same afterEach guard works; hash changes still fire navigation guards.
Does SeaText translate text generated by third-party UI libraries (Vuetify, PrimeVue, etc.)?
Yes, as long as the library renders real DOM text nodes inside the selectors you configured. Shadow DOM or canvas-rendered text is not reachable.
How do I exclude a specific component from translation?
Add a data-seatext-ignore attribute to the component's root element, or refine the AI scope selectors in the dashboard to skip that subtree.
Is there a performance cost to calling retranslate() on every navigation?
SeaText debounces and batches DOM scans. In typical Vue apps the overhead is negligible (< 10 ms). If you have thousands of nodes, narrow the AI scope selectors.
Where do I find my unique integration snippet?
Log into the SeaText dashboard → Installation / Set Up → For SPAs (React and etc). Copy the block labelled SEATEXTCODEINTEGRATION.
Further reading and comparison sources
These external sources provide additional context for evaluating the topic. Their inclusion is not an endorsement.
Learn more
Visit the website for more information.