Route Function

Route Functions give us full programmatic flexibility to define our routing logic.

// /pages/film/admin.page.route.js

import partRegex from 'part-regex'

export default pageContext => {
  // Route Functions allow us to implement advanced routing such as route guards.
  if (! pageContext.user.isAdmin) {
    return false
  }
  const { url } = pageContext
  // We can use RegExp or any JavaScript tool/library we want.
  if (!partRegex`/film/${/[0-9]+/}/admin`.test(url)) {
    return false
  }
  filmId = url.split('/')[2]
  return {
    // We make `filmId` available as `pageContext.routeParams.filmId`
    routeParams: { filmId },
    // To resolve route conflicts, see "Precedence" section below.
    precedence: 10,
  }
}

Route Functions enable us to use any string matching utility we want, such as partRegex.

Always Run

Upon each page request (i.e. upon each renderPage() call), vite-plugin-ssr runs all our Route Functions.

That's because we may have defined a Route Function with high precedence that overrides all other routes:

// login.page.route.js

export default pageContext => {
  const userIsAuthenticated =  pageContext.user !== null
  if (userIsAuthenticated) {
    return false
  }
  // If the user is not authenticated, then we render the login page.
  // (This is a convenient trick to preserve the URL.)
  return {
    // We override all other routes by setting a high `precedence` value of `99`.
    precedence: 99
  }
}

(FYI, this auth redirection trick is further explained at Guides > Page Redirection > Auth Redirection.)

vite-plugin-ssr cannot know whether a Route Function with a high precedence will override all other routes; that's why vite-plugin-ssr always runs all Route Functions upon each page request.

Precedence

If the route of two pages both match the same URL then we have a route conflict: vite-plugin-ssr cannot know which of the two pages should be rendered for that URL.

Upon route conflicts, vite-plugin-ssr chooses the first route in following order:

  1. Route Function, returned high positive precedence number
  2. Route Function, returned low positive precedence number
  3. Filesystem Routing
  4. Route String, static (without :param segment, e.g. /about/company)
  5. Route Function, returned no precedence number
  6. Route String, parameterized (with :param segements, e.g. /product/:productId or /product/*)
  7. Route Function, returned low negative precedence number (e.g. -1)
  8. Route Function, returned high negative precedence number (e.g. -99)

Example (4) + (6) + (7):

// product/item.page.route.js
export default '/product/:productId'
// product/list.page.route.js
export default '/product'
// product/catch-all.page.route.js
export default pageContext => {
  if (!pageContext.url.startsWith('/product/')) return false
  return {
    precedence: -1,
    pageContext: {
      // E.g. redirect `/product/wrong/url` to `/product`
      redirectTo: '/product'
    }
  }
}
URL                           MATCHES                                    CHOSEN
==================            ===================================        ======
/product/42                   product/item.page.route.js      (6)        X
                              product/catch-all.page.route.js (7)
URL                           MATCHES                                    CHOSEN
==================            ===================================        ======
/product                      product/list.page.route.js      (4)        X
                              product/catch-all.page.route.js (7)
URL                           MATCHES                                    CHOSEN
==================            ===================================        ======
/product/wrong/url            product/catch-all.page.route.js (7)        X

4: Route String, static (without :param segment, e.g. /about/company)
6: Route String, parameterized (with :param segements, e.g. /product/:productId or /product/*)
7: Route Function, returned low negative precedence number (e.g. -1)

Example (1) + (4):

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

export default pageContext => {
  if( pageContext.user === null ) {
    return {
      precedence: 99
    }
  }
  return false
}
URL                   pageContext.user       MATCHES                       CHOSEN
======                ================       =======================       ======
/admin                null                   login.page.route.js (1)       X
                                             admin.page.route.js (4)
URL                   pageContext.user       MATCHES                       CHOSEN
======                ================       =======================       ======
/admin                'brillout'             admin.page.route.js (4)       X

1: Route Function, returned high positive precedence number
4: Route String, static (without :param segment, e.g. /about/company)