Page Redirection

Vite-plugin-ssr doesn't have a built-in redirect mechanism, but it provides enough control: we can implement URL redirections ourselves.

URL redirections (aka "page redirections") are commonly used to protect private pages by redirecting unauthenticated users (e.g. from /admin to /login). With vite-plugin-ssr, we can use a Route Function instead of doing such URL redirection, see Authentication.

For other use cases, we can implement redirections on the server-side (see Server-side) as well as on the client-side (see Client-side).

A frequent problem with client-side redirections are hydration mismatches, see Hydration Mismatch.

Authentication

Instead of using URL redirections to protect private pages from unauthenticated users, we can use a Route Function to show a different page depending on whether the user is logged-in or not:

// /pages/login.page.route.js

// Route Function to render the login page to unauthenticated users, regardless of the URL.

export default pageContext => {
  // Only render the login page to unauthenticated users
  if (userIsLoggedIn(pageContext)) {
    return false
  }

  return {
    // We use a high precedence number of `99` to override all other routes.
    // This means that unauthenticated users always see the login page,
    // regardless of the URL.
    precedence: 99
  }
}

function userIsLoggedIn(pageContext) {
  return pageContext.user !== null
}

See Routing > Routing Precedence.

After the user successfully logs in, we reload the page with window.location.reload() (Server Routing) or navigate(window.location.pathname) (Client Routing).

This approach preserves the URL during the entire login flow:

  1. Unauthenticated user goes to URL /admin and sees the Login Page. (URL is /admin.)
  2. User fills the sign-in form and successfully logs in. (URL is still /admin.)
  3. Page is reloaded and the user now sees the Admin Page. (URL is still /admin.)

The original intention of the user is preserved and there is no need for some complex URL restoration mechanism.

Instead of using this approach, we can also use the more traditional approach of performing a page redirection. See sections below.

Server-side

We can perform a URL redirection by using pageContext:

// /renderer/_default.page.server.js
// Environment: server

export function render() {
  if (someCondition) {
    return {
      documentHtml: null,
      pageContext: {
        redirectTo: '/some/url'
      }
    }
  }

  // The usual stuff
  // ...
}
// server.js

app.get('*', async (req, res, next) => {
  const pageContextInit = { urlOriginal: req.url }
  const pageContext = await renderPage(pageContextInit)
  if (pageContext.redirectTo) {
    res.redirect(307, '/movie/add')
  } else if (!pageContext.httpResponse) {
    return next()
  } else {
    const { body, statusCode, contentType } = pageContext.httpResponse
    res.status(statusCode).type(contentType).send(body)
  }
})

We can also trigger a page redirection from onBeforeRender():

// movie.page.route.js
export default "/star-wars/@movieId"
// movie.page.server.js

export { onBeforeRender }

async function onBeforeRender(pageContext) {
  const movie = await fetchMovie(pageContext.routeParams.movieId)
  // If the user goes to `/movie/42` but there is no movie with ID `42` then
  // we redirect the user to `/movie/add` so he can add a new movie.
  if (movie === null) {
    return {
      pageContext: {
        redirectTo: '/movie/add'
      }
    }
  }
}
// /renderer/_default.page.server.js
// Environment: server

export { render }

function render(pageContext) {
  const { redirectTo } = pageContext
  if (redirectTo) {
    return {
      pageContext: {
        redirectTo
      }
    }
  }

  // The usual stuff
  // ...
}

Note that vite-plugin-ssr doesn't know anything about pageContext.redirectTo: it's a custom pageContext value.

Client-side

If we use Client Routing, then we can also redirect on the client-side.

// /renderer/_default.page.server.js
// Environment: server

// We make `pageContext.redirectTo` available to the browser for client-side redirection
export const passToClient = [/*...*/, 'redirectTo']
// /renderer/_default.page.client.js
// Environment: browser

import { navigate } from 'vite-plugin-ssr/client/router'

export function render(pageContext) {
  const { redirectTo } = pageContext
  if (redirectTo) {
    navigate(redirectTo)
    return
  }

  // The usual stuff
  // ...
}

Note that vite-plugin-ssr doesn't know anything about pageContext.redirectTo: it's a custom pageContext value.

Hydration Mismatch

If we use Client Routing we may end up with a hydration mismatch.

To remove the hydration mismatch, we may need to redirect on both the client-side and server-side.

There are situations when server-side redirection isn't an option and we can only redirect on the client-side. In such case we can suppress the hydration mismatch warning. Alternatively, we can also first hydrate the original page (before redirecting) and then redirect and render the new page.