⚠
vite-plugin-ssr has been renamed Vike, see migration guide.

Route Function

Route Functions give you full programmatic flexibility to define routes.

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

import partRegex from 'part-regex'

export default (pageContext) => {
  // Route guard (we can also use a guard() hook instead)
  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 }
  }
}

If you merely want to guard your page, then you can use a Route String with a guard() hook instead of a Route Function, which is usually a better approach.

You can use any routing tool you want, such as:

Precedence

The precedence number can be used to resolve route conflicts, see Routing > Routing Precedence.

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

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

resolveRoute()

You can use Route Strings inside 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.

Vite-plugin-ssr executes all Route Functions every time the user navigates to a new page. This means that a slow Route Function slows down all 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 Guides > Authentication > Login flow.

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 you use Client Routing, then all your Route Functions are loaded in the browser. This means that if a Route Function imports a lot of code, then all that code is loaded on the client-side of every page. A heavy Route Function slows down your whole app.

Route Functions should be lightweight and fast.

Async

Async Route Functions are forbidden.

// *.page.route.js

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

The motivation for having an asynchronous Route Function is usually to implement redirection and/or protecting a private page. Instead use throw render() or throw redirect() with an async guard() or onBeforeRender() hook.

An asynchronous Route Function would slow down your entire app: as explained in Lightweight & fast, every time the user navigates to a new page all Route Functions are called. This means that a slow Route Function slows down all pages.

Redirection

See API > throw redirect().

Domain-driven file structure

As with Filesystem Routing a domain-driven file structure is possible, such as:

FILESYSTEM
user/pages/list.page.js
user/pages/list.page.route.js
user/pages/create.page.js
user/pages/create.page.route.js
todo/pages/list.page.js
todo/pages/list.page.route.js
todo/pages/create.page.js
todo/pages/create.page.route.js

Cannot provide pageContext

Using Route Functions to provide pageContext values is forbidden.

// *.page.route.js

export default () => {
  return {
    // This is forbidden 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, you should keep Route Functions simple and you shouldn't implement complex logic in .page.route.js files.

That said, you can work around it by misusing pageContext.routeParams to provide data.

// *.page.route.js

export default () => {
  return {
    routeParams: {
      // Any data can be added here
    }
  }
}

But this isn't recommended: pageContext.routeParams is supposed to hold only a minimal amount of information. Instead, we recommend to implement complex logic in onBeforeRender(), guard(), or in a custom hook.