Routing Precedence

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

Upon Route String conflicts, vite-plugin-ssr chooses the first route from most specific to least specific. For example:

  1. /about/team (most specific: it matches only a single URL)
  2. /about/@path (less specific: it also matches /about/company, /about/vision, ...)
  3. /about/* (less specific: it also matches /about/some/nested/path)
  4. /* (least specific: it matches all URLs)

More examples at resolvePrecedence/route-strings.spec.ts.

Upon conflicts between Filesystem Routing, Route Strings and Route Functions, vite-plugin-ssr chooses the first route in following order:

  1. Route Function, returned high positive precedence number (e.g. 99)
  2. Route Function, returned low positive precedence number (e.g. 1)
  3. Filesystem Routing
  4. Route String, static (i.e. without @param segment, e.g. /about/company)
  5. Route Function, returned no precedence number (or 0)
  6. Route String, parameterized (i.e. with @param segement, 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.urlPathname.startsWith('/product/')) return false
  return {
    precedence: -1,
    pageContext: {
      // E.g. redirect `/product/wrong/url` to `/product`
      redirectTo: '/product'
    }
  }
}
URL                           MATCHES                                    WINNER
==================            ===================================        ======
/product/42                   product/item.page.route.js      (6)        ⬅️
                              product/catch-all.page.route.js (7)
URL                           MATCHES                                    WINNER
==================            ===================================        ======
/product                      product/list.page.route.js      (4)        ⬅️
                              product/catch-all.page.route.js (7)
URL                           MATCHES                                    WINNER
==================            ===================================        ======
/product/wrong/url            product/catch-all.page.route.js (7)        ⬅️

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                       WINNER
======                ================       =======================       ======
/admin                null                   login.page.route.js (1)       ⬅️
                                             admin.page.route.js (4)
URL                   pageContext.user       MATCHES                       WINNER
======                ================       =======================       ======
/admin                'brillout'             admin.page.route.js (4)       ⬅️

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

More examples at resolvePrecedence/overall.spec.ts.