Route Function

Route Functions give us full programmatic flexibility to implement advanced routing logic.

// /pages/product/edit.page.route.js

import partRegex from 'part-regex'

export default (pageContext) => {
  // Route guard
  if (!pageContext.user.isAdmin) {
    return false
  }

  // We can use RegExp / any library we want
  if (!partRegex`/product/${/[0-9]+/}/edit`.test(pageContext.urlPathname)) {
    return false
  }
  const id = pageContext.urlPathname.split('/')[2]

  return {
    // Make `id` available as pageContext.routeParams.id
    routeParams: { id }
  }
}

We can use any routing tool we want, such as:

Precedence

We can return a precedence number to resolve route conflicts, see Routing > Routing Precedence.

// /pages/product/edit.page.route.js

export default (pageContext) => {
  // ...
  return {
    precedence: 10,
  }
}

resolveRoute()

We can use resolveRoute() to resolve Route Strings in Route Functions:

// /pages/product/edit.page.route.js

import { resolveRoute } from 'vite-plugin-ssr/routing'

export default (pageContext) => {
  if (!pageContext.user.isAdmin) {
    return false
  }
  return resolveRoute('/product/@id/edit', pageContext.urlPathname)
}
// /pages/product/index.page.route.js

import { resolveRoute } from 'vite-plugin-ssr/routing'

export default (pageContext) => {
  {
    const result = resolveRoute('/product/@id', pageContext.urlPathname)
    if (result.match) {
      result.routeParams.view = 'overview'
      return result
    }
  }

  const result = resolveRoute('/product/@id/@view', pageContext.urlPathname)
  if (!['reviews', 'pricing'].includes(result.routeParams.view)) {
    return false
  }
  return result
}

Lightweight & fast

Route Functions should be lightweight and fast.

Because vite-plugin-ssr executes all Route Functions every time the user navigates to a new page. This means that a slow Route Function will slow down all our pages.

Vite-plugin-ssr always has to run all Route Functions because it cannot magically predict the outcome of Route Functions. Consider following example:

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

export default pageContext => {
  // Only render the login page to unauthenticated users
  if (pageContext.user !== null) return false
  return {
    // We override all other routes by setting a high `precedence` value of `99`.
    // This means that, if the user isn't authenticated, then *all* URLs render the login page.
    precedence: 99
  }
}

This authentication routing trick is further explained at Routing > Page Redirection > Authentication.

Vite-plugin-ssr cannot know whether another Route Function will return a higher precedence number, therefore vite-plugin-ssr has to execute all Route Functions.

If we use Client Routing, then all our Route Functions are loaded in the browser. This means that if a Route Function imports a lot of code, then all the code is loaded in the browser and therefore slows down the page load of each page.

Thus, we should keep Route Functions both fast and lightweight.

Async

Async Route Functions are forbidden.

// *.page.route.js

// ❌ This is forbidden
export default async () => { /* ... */ }

The motivation for asynchronous Route Functions is often redirections, but using throw RenderErrorPage() is usually a better approach.

An async Route Function would slow down our entire app: as we have seen in Lightweight & fast, every time the user navigates to a new page all Route Functions are called. This means that a slow Route Function would slow down all our pages.

Redirection & 404

See throw RenderErrorPage().

Cannot provide pageContext

Using Route Functions to provide pageContext values is forbidden.

// *.page.route.js

export default () => {
  return {
    // This is forbiden and vite-plugin-ssr will throw an error
    pageContext: {
      some: 'value'
    }
  }
}

In principle, vite-plugin-ssr could support providing pageContext values but it deliberately doesn't support it in order to foster lightweight Route Functions.

As explained in Lightweight & fast, we should keep Route Functions simple, i.e. we shouldn't implement complex logic in .page.route.js files.

That said, we can work around the limitation by providing values to pageContext.routeParams instead:

// *.page.route.js

export default () => {
  return {
    routeParams: {
      // We can add any data here
    }
  }
}

But this isn't recommended: pageContext.routeParams is supposed to hold only a minimal amount of information. Instead, we should prefer implementing complex logic in onBeforeRender() and/or custom exports/hooks.