Route rules
Say which navigation gets which transition, and SSGOI works out the direction and scroll from that.
drill(), slide() and the rest describe motion. The rule around one decides which navigations it covers, which way is forward, and what happens to scroll. Pick the relationship first.
| Rule | Use it for | Example |
|---|---|---|
on + except | A route family or a drill-down scope. | /posts ↔ /posts/42 |
from / to | One precise relationship between two screens. | /gallery ↔ /photo/42 |
ordered | Tabs or steps whose position decides direction. | feed → search → profile |
on, except, from and to each take one pattern or an array of patterns, and the best-matching entry in the array is the one that scores. ordered is the exception: one pattern per slot, no arrays inside it.
The three forms
The way you write the relationship is the forward direction.
const config = {
transitions: [
// Entering the family is forward, leaving it is backward.
{
on: "/posts/**",
except: "/posts",
transition: drill(),
},
// The written pair is forward. The reverse also matches.
{
from: "/gallery",
to: "/photo/*",
transition: zoom(),
},
// A rising index is forward.
{
ordered: ["/tabs/feed", "/tabs/search", "/tabs/profile"],
transition: slide(),
},
],
};A from/to pair matches in both orientations. Set bidirectional: false when only the orientation you wrote should match for a fresh navigation. Browser Back can still undo an observed entry using its recorded effect.
In an ordered array both routes have to land on different entries; if they resolve to the same one, the rule does not match.
Browser Back to a recorded entry's source uses the same effect with reversed direction and scroll endpoints. Browser Forward replays the original selection. Links and push() match current rules, even when revisiting the previous URL. Within one on scope that fresh visit is forward. Direct entries and unrecorded history pairs fall back to current rules.
With an explicit gallery → photo/* pair, a new link from detail back to gallery is backward even when it uses push rather than browser Back. If both from and to are photo/*, their equal patterns defer to history: a related-photo push is forward and Back reverses the recorded visit. The transition consumes this direction unchanged; zoom and hero enter/exit markers identify element roles, not another direction.
Path patterns
Use exact paths until the relationship really is a family.
| Pattern | Matches |
|---|---|
/posts | Only /posts. |
/posts/* | Exactly one more segment: /posts/42, but not /posts/42/edit. |
/posts/:id | The same one segment as *, but ranked higher when two patterns compete. [id] and {id} work too; the name is never captured. |
/posts/** | /posts itself plus any number of segments under it. ** only works as the last segment — a pattern like /a/**/b matches nothing. |
/** | Every path. A bare * is an alias for it. |
/search?q=a apart from /search. Partial-segment globs such as /posts/p* are not supported.When two rules match
Higher priority wins, then the more specific path, then the rule you declared first.
A rule with no priority is 0, so a negative value parks a broad fallback below everything else.
transitions: [
{ priority: -100, on: "/**", transition: fade() },
{ on: "/posts/**", except: "/posts", transition: drill() },
]Note the last step: the earliest rule wins a tie, so a later duplicate never overrides an earlier one.
Specificity is also not one shared scale. Each side of a rule scores its matched path — 100 per literal segment, :id 10, * 1, ** 0, and a whole-path exact match jumps above all of them. An on rule then takes the higher of its two endpoints, while from/to and ordered take the sum. A pair rule wins whenever its two sides add up past the onrule's single best side. If the outcome matters, set priority rather than reasoning about the score.
If nothing matches, nothing animates — and the navigation is not side-effect free. The arriving page is reset to the top, and the page you left has its recorded scroll position discarded, so an unmatched navigation also costs you the restore on the way back. Keep broad fallbacks at a low priority so they fill that gap.