Environment: Browser

By default vite-plugin-ssr does Server Routing. We can do Client Routing instead by using useClientRouter() instead of getPage().

In general, we recommend Server Routing, see Server Routing VS Client Routing.

React example:

Vue example:

Example showcasing all useClientRouter()'s options:

// Environment: Browser

import { render, hydrate } from 'some-view-framework'
import { useClientRouter } from 'vite-plugin-ssr/client/router'

const { hydrationPromise } = useClientRouter({
  async render(pageContext) {
    // `pageContext.isHydration` is set by `vite-plugin-ssr` and is `true` when the page
    // is already rendered to HTML.
    if (pageContext.isHydration) {
      // When we render the first page. (Since we do SSR, the first page is already
      // rendered to HTML and we merely have to hydrate it.)
      await hydrate(pageContext.Page)
    } else {
      // When the user navigates to a new page.
      await render(pageContext.Page)

  // If `ensureHydration: true` then `vite-plugin-ssr` ensures that the first render is always
  // a hydration. I.e. the first `render()` call is never interrupted — even if the user clicks
  // on a link. Default value: `false`.
  // If we use Vue, we set `ensureHydration: true` to avoid "Hydration Mismatch" errors.
  // If we use React, we leave `ensureHydration: false` for a slight performance boost.
  ensureHydration: true,

  // To create custom page transition animations

hydrationPromise.then(() => {
  console.log('Hydration finished; page is now interactive.')

function onTransitionStart() {
  console.log('Page transition start')
  // For example:
function onTransitionEnd() {
  console.log('Page transition end')
  // For example:

We can keep using <a href="/some-url"> links: link clicks are automatically intercepted. We can also use navigate() to programmatically navigate our user to a new page.

By default, the Client-side Router scrolls to the top of the page on page change; we can use <a keep-scroll-position /> / navigate(url, { keepScrollPosition: true }) if we want to preserve the scroll position instead. (Useful for Nested Routes.)

useClientRouter() is fairly high-level, if you need lower-level control, then open a GitHub issue.

Also see:

Edit this page