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
.
With vite-plugin-ssr, instead of doing a URL redirection, we can use a Route Function:
// pages/login.page.route.js
export default pageContext => {
const isUserAuthenticated = pageContext.user !== null
if (!isUserAuthenticated) {
return {
// We use a high precedence to override all other routes. This means that unauthenticated
// users always see the Login Page `login.page.js` (no matter the url). For example, if
// the user is unauthenticated and goes to `/admin`, then he'll see the Login Page.
precedence: 999
}
} else {
// If the user is authenticated then do not show the Login Page. For example, if the
// user is authenticated and goes to `/admin`, then he'll see the Admin Page.
return false
}
}
See Routing > Route Function > Precedence.
After the user successfully logs in, we reload the page with window.location.reload()
(Server Routing) or navigte(window.location.pathname)
(Client Routing).
This approach preserves the URL during the entire login flow:
/admin
and sees the Login Page. (URL is /admin
.)/admin
.)/admin
.)This works for all URLs: the original intention of the user is preserved and there is no need for a 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.
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
// ...
}
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
// ...
}
})
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.