VitePress SEO audit

VitePress is a Vite and Vue.js powered static site generator for documentation. Used by Vue, Vite, Vitest, Rollup, and hundreds of major open-source projects. VitePress generates fully static HTML at build time — strong for crawlability — but requires explicit configuration for canonical tags, JSON-LD structured data, sitemap generation, and robots.txt. DeepSEOAnalysis validates your VitePress site's actual HTML output: frontmatter metadata coverage, structured data server-rendering, Core Web Vitals from real CrUX data, and AI visibility scoring.

Run free VitePress SEO audit →

What the audit checks on VitePress sites

VitePress frontmatter metadata and head configuration
VitePress pages receive their SEO metadata from two sources: page-level frontmatter (title, description, head array in each .md file) and global configuration in defineConfig() (titleTemplate, head array, transformPageData hook). The audit validates what is actually rendered in the static HTML: whether the <title> tag is populated and unique per page (pages without a frontmatter title fall back to the H1 heading, which may not be optimised for search); whether meta description is present and page-specific (a missing description triggers Google auto-generation from body text); whether OG title, OG description, and OG image are configured in the global head config or per-page frontmatter; and whether canonical tags are present. VitePress does not generate canonical tags automatically — they require explicit configuration via transformPageData returning a canonical link element. Missing canonical tags on documentation sites with multiple URL variants (cleanUrls on/off, trailing slash, version-prefixed URLs) create duplicate content risks.
URL routing and cleanUrls configuration
VitePress generates URLs from the file system: a file at docs/guide/installation.md produces /guide/installation (with cleanUrls: true) or /guide/installation.html (without it). The audit checks: whether cleanUrls is enabled (recommended for SEO-friendly URLs without .html extensions); whether the base config is set correctly for sub-directory deployments; whether rewrite rules in defineConfig() are consistent with the deployed URL structure; whether versioned documentation uses consistent URL patterns across versions; and whether language-prefixed URLs for multilingual VitePress setups have hreflang annotations. The slug quality of documentation URLs is determined by directory and file naming — file names like "getting-started.md" produce clean URLs, while "GettingStarted.md" or "getting_started.md" can produce capitalized or underscore URLs. VitePress lowercases paths on Unix file systems but the source file name determines the URL slug quality.
JSON-LD structured data via transformHead or page head
VitePress has no built-in JSON-LD output — structured data must be added through the transformHead config hook or per-page head frontmatter array. Both mechanisms produce server-rendered JSON-LD in the static HTML output, which is the correct approach. The audit validates: whether Article JSON-LD is present on documentation pages (headline from frontmatter title, datePublished from frontmatter date or git timestamp via transformPageData, dateModified from git lastUpdated, author from frontmatter or site config, image from OG image); whether FAQPage JSON-LD is present on pages structured as FAQ content (question-heading + answer paragraph format); whether BreadcrumbList JSON-LD reflects the directory hierarchy (each path segment as a ListItem with absolute URL and name from page title or directory title config); and whether Organization and WebSite JSON-LD are present in the layout head. Common VitePress JSON-LD errors: using relative URLs in @id and item properties (must be absolute), missing datePublished when the frontmatter date field is not defined, and Article headline exceeding 110 characters from long documentation titles.
VitePress sitemap plugin configuration and robots.txt
VitePress 1.0+ includes native sitemap generation via the sitemap option in defineConfig(). The audit validates: whether sitemap.xml is accessible and correctly formed; whether all published pages are included (draft pages excluded); whether lastmod dates are populated from git commit timestamps (requires lastUpdated: true in config); whether the sitemap excludes utility pages like /404.html, redirect stubs, or development-only pages via the transformItems filter; whether the sitemap is declared in robots.txt; and whether the robots.txt file is present in the public/ directory (VitePress serves public/ directory contents at the root path). Documentation sites commonly over-include or under-include in sitemaps: tag archive pages (generated by VitePress tag plugins) may have thin content and should be evaluated for noindex or sitemap exclusion; versioned documentation pages from prior versions may be appropriate to noindex.
Core Web Vitals for VitePress sites
VitePress generates static HTML at build time, which means TTFB is typically excellent — static files served from a CDN edge location return first bytes in under 100ms globally. The Vue.js hydration that runs after the static HTML is parsed is lightweight for most documentation sites. Key Core Web Vitals risks on VitePress sites: (1) LCP — if the page's largest above-fold element is a hero image (common on landing pages), ensure it has fetchpriority="high" and dimensions defined to prevent browser-computed layout reflow. Documentation text pages typically have text as the LCP element, which performs well. (2) CLS — Vue.js hydration adding or moving DOM elements after the static HTML paint can cause layout shift. Fonts loaded from external CDN (Google Fonts or similar) can cause FOUT. Use font-display: swap and preload important fonts. (3) INP — VitePress's default Vue client-side navigation (SPA routing) executes after initial hydration; complex sidebar interactions or search overlay animations can contribute to INP on mid-tier mobile devices. Measure with real CrUX field data — not just Lighthouse lab scores — to see actual user experience.
AI visibility and GEO score for VitePress documentation sites
VitePress's static HTML output is advantageous for AI visibility: all page content, headings, and any server-rendered JSON-LD is in the initial HTML response, making it immediately accessible to AI crawlers without JavaScript execution. The five AI visibility signals: (1) AI crawler access in robots.txt — add explicit User-agent: GPTBot, User-agent: ClaudeBot, User-agent: PerplexityBot allowances in public/robots.txt (VitePress defaults to no robots.txt unless you add one to public/); (2) llms.txt — add a static public/llms.txt file following the llms.txt spec with links to key documentation sections and your product/organization description; (3) FAQPage JSON-LD — add via transformHead for pages with question-format headings and answer content; (4) question-heading ratio — documentation sites naturally use question-format headings ("How do I configure X?", "What is Y?") which is strong for AI citation eligibility — audit the heading structure across pages; (5) content chunkability — VitePress's section-based Markdown structure (H2 and H3 headings with prose paragraphs under each) is ideal for AI crawler content chunking at under 400 words per section.

How to audit your VitePress documentation site

  1. Enter your VitePress documentation URL — homepage, getting started page, API reference page, or any content page.
  2. DeepSEOAnalysis validates the static HTML output: frontmatter metadata in the rendered head tags, JSON-LD structured data presence and property completeness, canonical tag configuration, robots.txt AI crawler access, and Core Web Vitals from real CrUX field data.
  3. Review findings across structured data, Core Web Vitals, and the five-signal GEO score — all specific to VitePress's static output architecture.
  4. Share the audit URL with your documentation team or maintainers — no login required to view results.

VitePress SEO frequently asked questions

Does VitePress have good SEO out of the box?
VitePress has strong SEO fundamentals by default: it generates fully static HTML at build time, which means every page is immediately crawlable without JavaScript rendering. The initial HTML response includes all content — headings, body text, internal links — making it friendly to both Googlebot\'s first-wave crawl and AI crawlers. Where VitePress requires configuration: (1) Frontmatter metadata — title and description must be set per-page in frontmatter or globally via defineConfig(); neither is populated automatically. Without frontmatter title, VitePress falls back to the page\'s H1; without description, the meta description is empty and Google auto-generates it from content. (2) Canonical tags — VitePress does not add canonical tags by default. Add them via the head config in defineConfig() using transformPageData to compute the canonical URL for each page. (3) JSON-LD structured data — no built-in JSON-LD output. Implement via transformHead or head frontmatter. (4) robots.txt — not generated automatically; add a static robots.txt to the public/ directory. The sitemap plugin (@vitepress/plugin-sitemap or the built-in sitemap config) is the main configuration needed for production deployments.
How do you add structured data JSON-LD to VitePress?
VitePress supports JSON-LD structured data through two approaches: (1) **transformHead in defineConfig()** — the transformHead hook receives each page\'s data (frontmatter, title, URL) and returns additional head tags. Use it to inject <script type="application/ld+json"> with Article, BreadcrumbList, or FAQPage schema populated from page frontmatter fields. Example in .vitepress/config.ts: transformHead({ pageData }) { const schema = { "@context": "https://schema.org", "@type": "Article", "headline": pageData.frontmatter.title, "datePublished": pageData.frontmatter.date }; return [["script", { type: "application/ld+json" }, JSON.stringify(schema)]]; }. (2) **Page-level frontmatter head** — add head array in frontmatter to inject schema on a specific page without a global hook. Both approaches produce server-rendered JSON-LD in the static HTML output — which is the correct implementation. The JSON-LD is in every page\'s HTML at build time, making it visible to Googlebot\'s first-wave crawl and to AI crawlers that parse initial HTML without executing JavaScript.
How do I generate a sitemap in VitePress?
VitePress has a built-in sitemap generation option since version 1.0: add sitemap: { hostname: "https://your-domain.com" } to defineConfig() and VitePress generates sitemap.xml at build time. For lastmod dates, add lastUpdated: true to defineConfig() — VitePress reads the git commit timestamp for each file and populates <lastmod> in the sitemap. Additional sitemap configuration: (1) transformItems — filter or modify sitemap entries (exclude /404.html, /draft-pages, per-URL priority and changefreq). (2) For VitePress 0.x, use the community @vitepress/plugin-sitemap or custom build script as the built-in sitemap was added in 1.0. After generating the sitemap, declare it in robots.txt: Sitemap: https://your-domain.com/sitemap.xml.
What is the cleanUrls option in VitePress and should I use it?
The cleanUrls: true option in VitePress defineConfig() removes .html extensions from all page URLs, so /getting-started.html becomes /getting-started. For SEO, clean URLs without .html extensions are standard practice — they\'re shorter, more readable in SERPs, and don\'t expose the implementation technology. Enable cleanUrls: true in production. Important: cleanUrls requires your hosting platform to serve files without the extension. On Vercel and Netlify, this works automatically. On Cloudflare Pages, it requires Pages routing configuration. On Apache, it requires mod_rewrite. On nginx, use try_files $uri $uri.html $uri/ =404. Verify that all internal links use clean URLs (no .html) when cleanUrls is enabled — VitePress handles this in the generated HTML, but any hardcoded hrefs in Vue components or markdown must be updated manually. Without consistent URL format, you risk trailing .html and clean URL versions being treated as separate pages with split link equity.