VitePress Static Export Without SSR Surprises
Pure static HTML output — what runs at build vs browser, and avoiding window is not defined.
Tested on: VitePress 1.6.3, no SSR server in production
Myth: VitePress needs Node server. Reality: vitepress build emits static files only.
Build = SSG
npm run docs:build
ls docs/.vitepress/dist
# index.html, assets/*.js, guide/**/index.html
Deploy dist to any static host. No vitepress serve in production.
Client-only code guard
Error in build:
ReferenceError: window is not defined
Custom component accessed window at module top level.
Fix:
<script setup>
import { onMounted } from 'vue'
onMounted(() => {
// window access here only
})
</script>
Or:
if (typeof window !== 'undefined') { ... }
theme index.ts setup
export default {
...DefaultTheme,
setup() {
if (typeof document === 'undefined') return
// client-only listeners
}
}
Dynamic imports
<script setup>
import { defineAsyncComponent } from 'vue'
const Chart = defineAsyncComponent(() => import('./Chart.vue'))
</script>
Chart won't render meaningful HTML in view-source — acceptable for interactive widgets.
mpaMode (optional)
VitePress 1.x default is SPA with prefetch. For maximum static simplicity, still one JS bundle hydrates.
Don't confuse with ssr: true — VitePress docs site doesn't use SSR by default.
Verify static
npm run docs:build
npx serve docs/.vitepress/dist -p 8080
# disconnect network → site still loads (except external fonts)
View source on /guide/foo/ — content HTML present (SEO OK).
CI check
grep -r "window." docs/.vitepress/theme --include="*.vue" --include="*.ts"
# audit top-level usage
Result
Hosted on S3 static website + CloudFront. Zero Node runtime. Lighthouse SEO 100.