From 42cecb9069dd64f282bc4a1530ad425289a27217 Mon Sep 17 00:00:00 2001 From: Jacek Date: Tue, 30 Jun 2026 22:21:13 -0500 Subject: [PATCH 1/3] fix(shared): suggest :path* subtree form in createRouteMatcher path types WithPathPatternWildcard now suggests `/dashboard/:path*` instead of `/dashboard(.*)`. The `:path*` form matches on path-segment boundaries, so it covers `/dashboard` and its subtree without also matching sibling routes like `/dashboardxyz`. Root is special-cased to `/:path*` to avoid a malformed `//:path*`. Backward compatible: the param is Autocomplete-wrapped, so existing `(.*)` patterns still type-check, and there is no runtime change. --- .changeset/path-matcher-segment-wildcard.md | 5 +++++ packages/shared/src/pathMatcher.ts | 4 +++- 2 files changed, 8 insertions(+), 1 deletion(-) create mode 100644 .changeset/path-matcher-segment-wildcard.md diff --git a/.changeset/path-matcher-segment-wildcard.md b/.changeset/path-matcher-segment-wildcard.md new file mode 100644 index 00000000000..7ad08ff33cd --- /dev/null +++ b/.changeset/path-matcher-segment-wildcard.md @@ -0,0 +1,5 @@ +--- +'@clerk/shared': patch +--- + +`createRouteMatcher()` route suggestions now use the `:path*` subtree form (e.g. `/dashboard/:path*`) instead of `(.*)`. Unlike `/dashboard(.*)`, which also matches sibling routes such as `/dashboardxyz`, `/dashboard/:path*` matches only `/dashboard` and its path-segment subtree. Existing `(.*)` patterns keep working; this only changes the type-level autocomplete suggestion. diff --git a/packages/shared/src/pathMatcher.ts b/packages/shared/src/pathMatcher.ts index d5e6e49f516..4ee5ccf42fc 100644 --- a/packages/shared/src/pathMatcher.ts +++ b/packages/shared/src/pathMatcher.ts @@ -1,7 +1,9 @@ import { pathToRegexp } from './pathToRegexp'; import type { Autocomplete } from './types'; -export type WithPathPatternWildcard = `${T & string}(.*)`; +// Suggests the `:path*` subtree form (e.g. `/dashboard/:path*`), which matches on +// path-segment boundaries. `/` is special-cased to `/:path*` to avoid a malformed `//:path*`. +export type WithPathPatternWildcard = T extends '/' ? '/:path*' : `${T & string}/:path*`; export type PathPattern = Autocomplete; export type PathMatcherParam = Array | RegExp | PathPattern; From 53a83bec8f5e98c828a59c24e408d6c88b3c9b75 Mon Sep 17 00:00:00 2001 From: Jacek Date: Thu, 2 Jul 2026 09:27:30 -0500 Subject: [PATCH 2/3] fix(shared): keep WithPathPatternWildcard intact, add WithPathSegmentWildcard --- .changeset/path-matcher-segment-wildcard.md | 3 ++- packages/nextjs/src/server/routeMatcher.ts | 4 ++-- packages/shared/src/pathMatcher.ts | 9 +++++++-- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/.changeset/path-matcher-segment-wildcard.md b/.changeset/path-matcher-segment-wildcard.md index 7ad08ff33cd..aedd7fca173 100644 --- a/.changeset/path-matcher-segment-wildcard.md +++ b/.changeset/path-matcher-segment-wildcard.md @@ -1,5 +1,6 @@ --- '@clerk/shared': patch +'@clerk/nextjs': patch --- -`createRouteMatcher()` route suggestions now use the `:path*` subtree form (e.g. `/dashboard/:path*`) instead of `(.*)`. Unlike `/dashboard(.*)`, which also matches sibling routes such as `/dashboardxyz`, `/dashboard/:path*` matches only `/dashboard` and its path-segment subtree. Existing `(.*)` patterns keep working; this only changes the type-level autocomplete suggestion. +`createPathMatcher()` and `createRouteMatcher()` route suggestions now use the `:path*` subtree form (e.g. `/dashboard/:path*`) instead of `(.*)`. Unlike `/dashboard(.*)`, which also matches sibling routes such as `/dashboardxyz`, `/dashboard/:path*` matches only `/dashboard` and its path-segment subtree. The new suggestion type is exported as `WithPathSegmentWildcard`; the existing `WithPathPatternWildcard` type is unchanged (now deprecated), and `(.*)` patterns keep working. This only changes the type-level autocomplete suggestion. diff --git a/packages/nextjs/src/server/routeMatcher.ts b/packages/nextjs/src/server/routeMatcher.ts index 428fc3e9dec..15dd32cc8aa 100644 --- a/packages/nextjs/src/server/routeMatcher.ts +++ b/packages/nextjs/src/server/routeMatcher.ts @@ -1,10 +1,10 @@ -import { createPathMatcher, type WithPathPatternWildcard } from '@clerk/shared/pathMatcher'; +import { createPathMatcher, type WithPathSegmentWildcard } from '@clerk/shared/pathMatcher'; import type { Autocomplete } from '@clerk/shared/types'; import type Link from 'next/link'; import type { NextRequest } from 'next/server'; type NextTypedRoute['0']['href']> = T extends string ? T : never; -type RouteMatcherWithNextTypedRoutes = Autocomplete | NextTypedRoute>; +type RouteMatcherWithNextTypedRoutes = Autocomplete | NextTypedRoute>; export type RouteMatcherParam = | Array diff --git a/packages/shared/src/pathMatcher.ts b/packages/shared/src/pathMatcher.ts index 4ee5ccf42fc..d7a87a44cdf 100644 --- a/packages/shared/src/pathMatcher.ts +++ b/packages/shared/src/pathMatcher.ts @@ -1,10 +1,15 @@ import { pathToRegexp } from './pathToRegexp'; import type { Autocomplete } from './types'; +/** + * @deprecated Prefer {@link WithPathSegmentWildcard}; `(.*)` also matches sibling routes + * (e.g. `/dashboard(.*)` matches `/dashboardxyz`). + */ +export type WithPathPatternWildcard = `${T & string}(.*)`; // Suggests the `:path*` subtree form (e.g. `/dashboard/:path*`), which matches on // path-segment boundaries. `/` is special-cased to `/:path*` to avoid a malformed `//:path*`. -export type WithPathPatternWildcard = T extends '/' ? '/:path*' : `${T & string}/:path*`; -export type PathPattern = Autocomplete; +export type WithPathSegmentWildcard = T extends '/' ? '/:path*' : `${T & string}/:path*`; +export type PathPattern = Autocomplete; export type PathMatcherParam = Array | RegExp | PathPattern; export class MalformedURLError extends Error { From 42aee20a49d41e8da8f9e2f3843e1cd0638c2ef3 Mon Sep 17 00:00:00 2001 From: Jacek Date: Thu, 2 Jul 2026 12:22:30 -0500 Subject: [PATCH 3/3] fix(shared): address path wildcard review comments --- packages/shared/src/pathMatcher.ts | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/packages/shared/src/pathMatcher.ts b/packages/shared/src/pathMatcher.ts index d7a87a44cdf..dc9c901f4a1 100644 --- a/packages/shared/src/pathMatcher.ts +++ b/packages/shared/src/pathMatcher.ts @@ -6,9 +6,20 @@ import type { Autocomplete } from './types'; * (e.g. `/dashboard(.*)` matches `/dashboardxyz`). */ export type WithPathPatternWildcard = `${T & string}(.*)`; -// Suggests the `:path*` subtree form (e.g. `/dashboard/:path*`), which matches on -// path-segment boundaries. `/` is special-cased to `/:path*` to avoid a malformed `//:path*`. -export type WithPathSegmentWildcard = T extends '/' ? '/:path*' : `${T & string}/:path*`; + +type StripTrailingSlash = T extends '/' + ? T + : T extends `${infer Prefix}/` + ? StripTrailingSlash + : T; + +/** + * Suggests the `:path*` subtree form (e.g. `/dashboard/:path*`), which matches on + * path-segment boundaries. `/` is special-cased to `/:path*` to avoid a malformed `//:path*`. + */ +export type WithPathSegmentWildcard = T extends '/' + ? '/:path*' + : `${StripTrailingSlash}/:path*`; export type PathPattern = Autocomplete; export type PathMatcherParam = Array | RegExp | PathPattern;