Guide Vue SEO

Vue 3 defaults à rendu côté client, donc le contenu n’est pas dans le raw HTML. Ce est comment faire Vue apps crawlable et indexable — router mode, @unhead/vue, prerendering avec vite-ssg, SSR/SSG avec Nuxt, hydration, et Core Web Vitals.

Première publication : 26 juin 2026 · Dernière mise à jour : 3 août 2026 · Avancé

Vue 3 defaults à rendu côté client, donc a plain Vite + Vue app ships an vide HTML shell et construit lune page dans le navigateur. Google peut rendre il — mais rendu est delayed et async données peut être missed, et Bing et social scrapers souvent ne peut pas exécuter le JS à tout. Le corrections: utiliser Vue Router history mode (pas hash mode), manage <head> avec @unhead/vue, et put contenu dans le HTML avec prerendering (vite-ssg), SSR, or SSG via Nuxt. Dynamic rendu est deprecated — ne pas construire on il.

«> TL;DR — Vue 3 is CSR by default, so the content isn’t in the raw HTML —

Google renders it later via its evergreen Chromium WRS, but the render is queued and async data fetched in onMounted can be missed, and Bing/social scrapers often don’t run the JS at all. Non-negotiables: Vue Router createWebHistory() (hash mode has “a bad impact in SEO”), <head> via @unhead/vue / useSeoMeta(), and a rendering strategy that gets content into HTML — vite-ssg prerendering for content-stable sites, Nuxt for full SSR/SSG. Dynamic rendering is deprecated; prerender-spa-plugin is legacy. Watch hydration mismatches (a server/client content delta is an SEO problem, not just a perf one) and the CSR bundle’s hit on LCP. For the framework-agnostic version of all of this, see JavaScript SEO. » (Traduction) (Résumé en français de la section dix-sept, sous-partie un : le texte source est conservé pour vérification lors de la relecture francophone.)

The par défaut Vite + Vue scaffold is client-side rendered

A plain npm create vue@latest app (Vite + Vue) ships a near-empty HTML shell et a JavaScript bundle. Le navigateur executes que bundle à construire le DOM. Ce est fine pour utilisateurs et a problème pour robots d’exploration: le raw HTML contient presque aucun contenu, et tout dépend on rendu.

c’est a property de le par défaut scaffold, pas a ceiling on Vue itself — Vue core aussi prend en charge serveur rendu (createSSRApp) et static generation as premier-class paths, et Vue’s propre SSR guide steers la plupart production SSR/SSG fonctionner toward Nuxt plutôt que a hand-rolled setup. Donc avant reaching pour a corriger, vérifier ce que votre app est en réalité shipping: a bare Vite SPA avec aucun rendu config est CSR; le même app behind Nuxt or vite-ssg n’est pas.

«Vue’s own SSR guide is direct about the upside of not doing this: with server-side rendering, “the search engine crawlers will directly see the fully rendered page.” The flip side is what you ship by default — content that only exists after JavaScript runs. Preuve à l’appui de cette affirmation Vue server-side rendering sends rendered HTML so crawlers can directly see page content. Portée : Vue SSR benefits; does not guarantee indexing. Niveau de confiance : élevé · Vérifié : Vue: SSR guide » (Traduction) (Résumé en français de la section vingt et un, sous-partie un : le texte source est conservé pour vérification lors de la relecture francophone.)

(Vue 2 reached fin of life in December 2023; everything ici is Vue 3.)

Comment Googlebot handles a Vue app

Google processes JavaScript dans phases — explorer le raw HTML, alors rendre il dans an evergreen, headless Chromium instance (le Web Rendu Service), alors indexer le rendu résultat. Deux choses suivre depuis que:

  • Rendu est queued et delayed. Une page sits dans le rendre queue — généralement seconds à minutes, mais il peut spike. Contenu que exists seulement après JS exécute est pas immédiatement indexable; contenu dans le raw HTML est.
  • Async données est le réel risque. Synchronous JavaScript renders reliably. Données récupéré depuis an API après mount n’est pas guaranteed à être caught. Vue’s SSR guide dit il plainly: “Si votre app commence avec a chargement spinner, alors récupère contenu via Ajax, le robot d’exploration ne va pas wait pour vous à finish. Ce signifie si vous ont contenu récupéré asynchronously on pages où SEO est important, SSR pourrait être necessary.” Practically: données récupéré dans onMounted est client-seulement et invisible à robots d’exploration que ne pas rendre — et à risque même avec le ones que faire.

John Mueller a décrit le échec mode pour SPA-style setups où le static HTML est mostly identical et tout le unique contenu dépend on JavaScript: si que JS ne peut pas être executed correctement, lune pages fin up looking le même à Google, et le focus stays on le boilerplate HTML plutôt que le JS-chargé contenu. Le lesson est le même as le broader JavaScript SEO page: obtenir le contenu dans le DOM, rapide et reliably.

Bing et social bots sont pire à ce que Google. Bing a aucun rendu pipeline comparable à Google’s, et social preview scrapers (X, Slack, iMessage) généralement ne pas exécuter JavaScript à tout — donc a bare CSR Vue app produces vide share previews. Si Bing trafic or social sharing matters, prerendering or SSR n’est pas optional.

Vue Router: utiliser createWebHistory(), pas hash mode

«Vue Router has two history modes. Hash mode (createWebHashHistory()) produces URLs like example.com/#/about; Vue Router’s docs say it “does however have a bad impact in SEO” because everything after the # is a fragment the server ignores. Preuve à l’appui de cette affirmation Vue Router hash history uses a URL fragment that is not sent to the server and is discouraged for SEO. Portée : Vue Router hash history. Niveau de confiance : élevé · Vérifié : Vue Router: History modes Use HTML5 history mode: » (Traduction) (Résumé en français de la section vingt-neuf, sous-partie un : le texte source est conservé pour vérification lors de la relecture francophone.)

import { createRouter, createWebHistory } from 'vue-router'

const router = createRouter({
  history: createWebHistory(),
  routes: [/* ... */],
})

History mode donne clean URLs (example.com/about) but exige a server fallback — quelconque direct requête to a route has to serve index.html so Vue peut prendre over, sinon direct hits 404. Configurer que catch-all on votre host (Nginx try_files, an SPA rewrite on Netlify/Vercel/Cloudflare, etc.).

«Scope that rewrite carefully — a catch-all that’s too broad also serves index.html for missing static assets, real API routes, or pages you actually want to 404. That silently undoes the soft-404 guidance below and can hide a broken link behind a page that looks like it “worked.” » (Traduction) (Résumé en français de la section trente-deux, sous-partie un : le texte source est conservé pour vérification lors de la relecture francophone.)

«For genuine not-found states, avoid soft-404s — a client-side “not found” view that still returns 200 can get indexed as an empty shell. Google’s guidance is to either redirect to a URL that returns a real 404 status or add a <meta name="robots" content="noindex"> to error pages, and to use the History API for routing between views. » (Traduction) (Résumé en français de la section trente-trois, sous-partie un : le texte source est conservé pour vérification lors de la relecture francophone.)

«## Head and meta management: @unhead/vue » (Traduction) (Résumé en français de la section trente-quatre, sous-partie un : le texte source est conservé pour vérification lors de la relecture francophone.)

«By default every Vue route shares one <title> and one meta description. The tag history here matters because the old answers are dead ends: » (Traduction) (Résumé en français de la section trente-cinq, sous-partie un : le texte source est conservé pour vérification lors de la relecture francophone.)

  • vue-meta — le Nuxt 2-era library. Legacy.
  • @vueuse/head — son successor, maintenant sunset.
  • @unhead/vue — le actuel community standard, et ce que vous devez utiliser pour a non-Nuxt Vue 3 app.

Le ergonomic entry point est le useSeoMeta() composable — type-sûr, XSS-sûr, et aware de 100+ meta balises notamment Ouvrir Graph et Twitter Card:

import { useSeoMeta } from '@unhead/vue'

useSeoMeta({
  title: 'Vue SEO Guide',
  description: 'How to make a Vue 3 app crawlable and indexable.',
  ogTitle: 'Vue SEO Guide',
  ogDescription: 'How to make a Vue 3 app crawlable and indexable.',
  twitterCard: 'summary_large_image',
})

«Meta can be reactive — pass a getter or computed value and the tags update when your data does. For canonical tags, the safest pattern is still HTML, not JS: Google’s guidance is that “the best way to set the canonical URL is to use HTML,” and if you must inject it with JavaScript, always set it to the same value the HTML would. (I tested JS canonicals at Ahrefs and found Google does respect them — it even led Google to add an exception to its docs — but HTML is still the lower-risk path.) » (Traduction) (Résumé en français de la section trente-neuf, sous-partie un : le texte source est conservé pour vérification lors de la relecture francophone.)

Pick a rendering strategy

Ce est le décision que en réalité moves le needle. Même menu as le framework- agnostic JavaScript SEO page, applied à Vue.

Rendu côté client (CSR) — le par défaut, et le risky un. Acceptable pour app-like pages behind a login, dashboards, et n’importe quoi vous ne pas besoin indexé. Pas acceptable pour contenu que doit classer or être shared.

Prerendering avec vite-ssg. Pour contenu-stable Vue 3 SPAs, vite-ssg generates static HTML à construire temps — swap votre construire script depuis vite build à vite-ssg build. Il ships @unhead/vue construit dans. Great pour marketing sites, docs, et blogs; pas a substitute pour SSR on highly dynamic, per-requête pages. Remarque que le old webpack-era prerender-spa-plugin est legacy et effectively unmaintained — vite-ssg (or Nuxt) est le modern replacement.

«Server-side rendering. Vue’s official guide covers a manual SSR setup with @vue/server-renderer, but it explicitly steers most projects to a meta-framework rather than rolling your own — and for simple cases it even says “if you’re only investigating SSR to improve the SEO of a handful of marketing pages … then you probably want SSG instead of SSR.” If you do roll manual SSR, create the app, router, and store fresh per request: on a long-running Node server, module-level singletons get reused across requests, and mutating shared state with one user’s data can leak it into another user’s response. That’s one more reason Vue’s docs point most projects at Nuxt, which handles per-request isolation for you.

Preuve à l’appui de cette affirmation Module-scope singleton state in Vue SSR can leak user-specific data across requests; create application, router and store instances per request. Portée : SSR and hydration Niveau de confiance : élevé · Vérifié : Server-Side Rendering

» (Traduction) (Résumé en français de la section quarante-quatre, sous-partie un : le texte source est conservé pour vérification lors de la relecture francophone.)

Nuxt — le recommended complet solution. Nuxt donne vous SSR et SSG out de le box, built-dans useSeoMeta() et head management, et le @nuxtjs/seo module pour sitemaps, robots.txt, et données structurées. Pour la plupart Vue projects où SEO matters, ce est le chemin Vue’s propre docs point à. (Nuxt a son propre depth — covered séparément; treat ce as le cross-lien, pas le deep dive.)

Dynamic rendu — ne pas. Diffusion prerendered HTML à bots et le SPA à utilisateurs était toujours a workaround, et Google deprecated il dans 2024, removing le implementation docs. Construire on SSR or SSG à la place.

Pour documentation précisément, VitePress est le Vue-native static site generator. Chaque page ships as plain HTML — aucun JS rendu barrier — et SEO est configuré via frontmatter (title, description, head) or le transformHead construire hook pour canonical/dynamic balises.

Hydration mismatches are an SEO problem, pas simplement a perf un

Quand vous faire SSR/SSG, le client mounts avec createSSRApp() — pas le plain createApp() — et hydrates le serveur-rendu HTML au lieu de rebuilding il depuis scratch. Si le serveur HTML et le client rendre diverge, Vue discards et re-renders le mismatched nodes. Dans SEO terms, que signifie le contenu Google indexé depuis le serveur rendre peut differ depuis ce que le utilisateur voit après hydration — a qualité-signal inconsistency, pas simplement a flicker. Courant causes: invalide HTML nesting, random valeurs dans templates, et date/temps discrepancies entre serveur et client. Vue 3,5+ ajoute data-allow-mismatch à suppress le mismatch warning selectively où a difference est intentional — il quiets le console, il ne fait pas faire le serveur et client sortie equivalent, donc ne pas reach pour il simplement à silence a mismatch vous haven’t en réalité diagnosed.

Core Web Vitals sont a Vue-spécifique concern

Par défaut CSR Vue ships a grand JS bundle que a à download, parse, et execute avant le largest contenu paints — donc LCP suffers si vous ne faites pashing. Levers:

  • Code splitting (Vite’s par défaut) et lazy-chargé routes donc vous ne pas ship le entier app up front.
  • fetchpriority="high" on le LCP hero image.
  • Vapor Mode — an opt-dans compiler mode que bypasses le virtual DOM pour eligible components, qui peut cut hydration coût on SSR pages. c’est Vue 3,6, pas 3,5 — encore experimental (beta/release-candidate as de mid-2026, pas yet dans le stable 3,5.x line) — worth watching, pas quelque chose à depend on dans production aujourd’hui.

Données structurées in Vue

Google executes JavaScript avant reading données structurées, donc injecting JSON-LD fonctionne — @unhead/vue’s useHead() avec a script de type application/ld+json est le fiable modèle. Parce que il dépend on rendu, confirmer il avec Inspection d’URL plutôt que assuming Google saw il.

Où ce fits

Vue SEO est un instance de le general JavaScript SEO problème — parity, interaction, state, timing — et il shares presque tout avec le CMS headless situation, où le rendu mode de le frontend decides le résultat.

Un caveat que s’applique à chaque option ci-dessus: SSR et SSG obtenir votre contenu dans le HTML, mais aucun guarantees indexation, rankings, Core Web Vitals, or que hydration va match le serveur sortie exactly. Ils supprimer le JavaScript-rendu barrier — le rest de SEO (et correctness) est encore on vous.

Si vous remember un chose: obtenir votre contenu dans le HTML. Tout sinon est detail.

Ajouter une note d’expert

Épingler une citation d’expert

Nouvelle personne ? Créez son profil non revendiqué à /admin/experts/ → Épingler une citation d’expert d’abord.