Vue SEO: the fix that doesn't need Nuxt
A standard Vue 3 SPA ships an empty mounting div and a JavaScript bundle. Googlebot reads that response, sees no content, and decides the URL is blank before Vue has had a chance to mount.
This is a known problem, and the fix is simpler than you think.
Don't want an external tool? We understand.
Migrating to Nuxt for SSR is the official answer. It's also a routing rewrite, a server-runtime requirement, and weeks of debugging hydration warnings and browser-only assumptions. PageGlass is a five-minute alternative that leaves your Vue codebase exactly as it is.
Fix this in three steps
- Run a free Vue scan. Confirm the bot visibility gap caused by client-side Vue rendering - the scan takes ten seconds and needs no signup. You get exactly what Googlebot, Bingbot, and the AI crawlers see when they hit your URLs today, side by side with what your users see.
- Add one CNAME record. Point your Vue site's domain through PageGlass in your DNS provider. The change takes thirty seconds and is fully reversible. Zero code changes, no deploy, no impact on the bundle your real users download.
- Vue content is now indexable. Bot traffic now receives fully-rendered HTML for every URL on your Vue site, while human visitors keep getting the exact same dynamic experience as before. Come back to PageGlass and run the scan again to confirm the fix.
Where Vue SEO actually breaks
A standard Vite + Vue 3 project produces an index.html that
looks roughly like this in production: a <head> with
static defaults, a <body> containing
<div id="app"></div>, and a script tag pointing
at the JavaScript bundle. Everything you see in the browser - the H1,
the navigation, the dynamic content - is assembled by Vue after the
bundle parses and the app mounts.
Googlebot can render JavaScript, but on a delayed second pass that may happen days later or not at all. Bingbot, GPTBot, ClaudeBot, PerplexityBot, the social card fetchers, and most other crawlers don't execute JavaScript at all. They get the initial HTML and that's it. For a Vue SPA, the initial HTML rarely contains anything useful for ranking.
Common Vue fixes that don't actually fix it
- Setting meta tags inside onMounted. The hook runs after Vue mounts in the browser. Crawlers don't wait for that. The tags exist for human visitors and Slack but not for Googlebot.
- vue-meta or @vueuse/head used in components. Same problem. Tags are produced after hydration, not before the response leaves the server.
- Pre-rendering only the home page with vite-plugin-prerender. Useful for landing pages but doesn't help with deep routes, product pages, or anything driven by route params and runtime data.
- Trusting that "Google can run JavaScript" means everything is fine. Google can run JavaScript on a delayed schedule for some pages. That's not the same as guaranteeing every Vue route gets indexed correctly, especially under crawl-budget constraints.
What actually works
Three paths: (1) adopt Nuxt SSR for proper server-side rendering, the long-term right answer if your team has weeks for the migration; (2) use Vite SSR or vite-plugin-ssr for a lighter-touch SSR setup that keeps more of your existing structure; or (3) front your Vue app with PageGlass - a single CNAME change in your DNS sends bot traffic through a renderer that runs your Vue app in a headless browser and serves the fully-rendered HTML. Human visitors continue to get the client-rendered experience.