Page Redirection

Auth Redirection

URL redirections are often about protecting URLs from unauthenticated users: if a user is not authenticated and goes to /admin then we redirect the user to /login.

Instead of doing a URL redirection, we can use a Route Function instead:

// admin.page.route.js

export const '/admin'
// login.page.route.js

export default pageContext => {
  const userIsAuthenticated =  pageContext.user !== null
  // If the user is authenticated and goes to `/admin`, then we render the Admin Panel
  if (userIsAuthenticated) {
    return false
  }
  // If the user is not authenticated and goes to `/admin`, then we render the Login Page
  return {
    // We override the Route String `/admin` of `admin.page.route.js`.
    // (Route Strings have a `precedence` value of `0`.)
    precedence: 1
  }
}

After the user has successfully completed the Login Form rendered at /admin, we can do a window.location.reload() and /admin will now render the Admin Panel. This means that the user always stays on /admin during the entire authentication flow.

However, we can still perform a page redirection if we want. See sections below.

Server-side

We can perform a URL redirection by using pageContext:

// _default.page.server.js

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

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

const renderPage = createPageRenderer(/*...*/)
app.get('*', async (req, res, next) => {
  const pageContextInit = { url: 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 a page:

// 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'
      }
    }
  }
}
// _default.page.server.js

export { render }

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

  // The usual stuff
  // ...
}

Client-side

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

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

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

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

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

    // The usual stuff
    // ...
  }
})

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 is not 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.