SSR Compatibility

Server-side rendering compatibility, ClientOnly component, and handling browser-only code

VitePress pre-renders pages on the server during build. All Vue code must be SSR-compatible.

The Rule

Only access browser/DOM APIs in Vue lifecycle hooks:

  • onMounted()

  • onBeforeMount()

<script setup>
import { onMounted, ref } from 'vue'

const windowWidth = ref(0)

onMounted(() => {
  // Safe - runs only in browser
  windowWidth.value = window.innerWidth
})
</script>

Do NOT access at top level:

<script setup>
// WRONG - runs during SSR where window doesn't exist
const width = window.innerWidth
</script>

ClientOnly Component

Wrap non-SSR-friendly components:

Libraries That Access Browser on Import

Some libraries access window or document when imported:

Dynamic Import in onMounted

Conditional Import

In enhanceApp

defineClientComponent

Helper for components that access browser on import:

With props and slots:

Teleports

Teleport to body only with SSG:

For other targets, use postRender hook:

Common SSR Errors

"window is not defined"

Code accesses window at module level:

"document is not defined"

Same issue with document:

Hydration Mismatch

Server and client render different content:

Checking Environment

Key Points

  • Access browser APIs only in onMounted or onBeforeMount

  • Use <ClientOnly> for non-SSR components

  • Use defineClientComponent for libraries that access browser on import

  • Check import.meta.env.SSR for environment-specific code

  • Teleport to body only, or use postRender hook

  • Consistent rendering prevents hydration mismatches

Last updated

Was this helpful?