@@ -750,11 +750,49 @@ KEYBOARD_SHORTCUTS: {
750750> and makes it harder to reason about which screens can trigger a given flow .
751751> When in doubt , prefer an explicit list .
752752
753- ### Current limitations ( work in progress )
753+ ### Optional path parameters
754754
755- - ** Optional path parameters :** Suffixes must not include optional path params (e .g . ` a/:reportID? ` ). Required path parameters (e .g . ` flag/:reportActionID ` ) and query parameters are supported - see [Dynamic routes with query parameters ](#dynamic - routes - with - query - parameters ).
755+ Dynamic route suffixes may declare optional path parameters by appending ` ? ` to the parameter name .
756+ An optional parameter can be present or absent in the URL ; when absent , the corresponding key is
757+ omitted from the navigation state ' s `params` (it is **not** set to `undefined`).
756758
757- If you try to use dynamic routes with optional path parameters now , you will either fail to navigate to the page at all or end up on a non - existent page , and the navigation will be broken .
759+ Optionals can appear anywhere in the suffix :
760+
761+ - ** Trailing optional :** ` member-details/:accountID? ` matches both ` member-details ` and ` member-details/123 ` .
762+ - ** Middle optional :** ` wrap/:p?/end ` matches both ` wrap/end ` and ` wrap/x/end ` .
763+ - ** Multiple optionals :** ` a/:p1?/b/:p2? ` matches ` a/b ` , ` a/x/b ` , ` a/b/y ` , and ` a/x/b/y ` .
764+
765+ #### Configuration example
766+
767+ ` ` ` ts
768+ DYNAMIC_ROUTES: {
769+ MEMBER_DETAILS: {
770+ path: 'member-details/:accountID?',
771+ entryScreens: [SCREENS.WORKSPACE.MEMBERS],
772+ getRoute: (accountID?: string) => (accountID ? ` member - details / $ {accountID }` : 'member-details'),
773+ },
774+ },
775+ ` ` `
776+
777+ #### Precedence rules
778+
779+ When several registered suffixes could match the same URL , the matcher uses a deterministic
780+ three - phase order . Each phase exhausts all sub - suffix lengths (longest to shortest ) before the
781+ next phase begins :
782+
783+ 1. ** Static beats everything .** All sub - suffix lengths are checked for an exact static match
784+ first . A short static match (e .g . single - segment ` country ` ) always beats a longer parametric
785+ match (e .g . ` :reportID/country ` ).
786+ 2. ** Strict parametric beats optional parametric .** After statics , all sub - suffix lengths are
787+ checked for strict parametric patterns (no optional params , e .g . ` flag/:reportID/:reportActionID ` ).
788+ Only after those are exhausted does the matcher try optional parametric patterns
789+ (e .g . ` page/:id? ` ).
790+ 3. ** Longest match wins within each phase .** Within a single phase the algorithm iterates from
791+ the longest candidate to the shortest , so a 3 - segment match beats a 2 - segment one when both
792+ are registered .
793+ 4. ** Among patterns of the same kind : first registered wins .** If multiple patterns of the same
794+ type (strict or optional ) could match the same candidate , the one declared first in
795+ ` DYNAMIC_ROUTES ` wins .
758796
759797### Multi - segment dynamic routes
760798
0 commit comments