diff --git a/docs/api/route-redirect.mdx b/docs/api/route-redirect.mdx
index f550440348..3fdbef3a9e 100644
--- a/docs/api/route-redirect.mdx
+++ b/docs/api/route-redirect.mdx
@@ -32,7 +32,9 @@ The route redirect has two configurable properties:
- `from`
- `to`
-It redirects "from" a URL "to" another URL. When the defined `ion-route-redirect` rule matches, the router will redirect from the path specified in the `from` property to the path in the `to` property. In order for a redirect to occur the `from` path needs to be an exact match to the navigated URL.
+It redirects "from" a URL "to" another URL. When the defined `ion-route-redirect` rule matches, the router will redirect from the path specified in the `from` property to the path in the `to` property. In order for a redirect to occur the `from` path needs to be an exact match to the navigated URL, unless `from` contains a `*`. A `*` matches the rest of the path from that segment on, as long as the path has at least as many segments as `from`, so `/admin/*` matches `/admin/posts` but not `/admin`.
+
+To render a "not found" page for unknown paths instead of redirecting, use a [catch-all route](route.mdx#catch-all-routes).
## Multiple Route Redirects {/* #multiple-route-redirects */}
@@ -58,7 +60,7 @@ If the user navigates to `/admin` the router will redirect to `/login` and stop
will NOT redirect if the user navigates to `/admin/posts` -->
-
```
diff --git a/docs/api/route.mdx b/docs/api/route.mdx
index aee676feb2..60191b2592 100644
--- a/docs/api/route.mdx
+++ b/docs/api/route.mdx
@@ -30,6 +30,121 @@ Note: this component should only be used with vanilla and Stencil JavaScript pro
:::
+## Catch-all Routes {/* #catch-all-routes */}
+
+:::note
+
+Catch-all routes are supported starting in Ionic v9.1. In earlier versions, `url="/:any"` can be used to catch paths whose first segment does not match another route. Unknown paths below a route that does match, such as `/admin/settings`, still render that route.
+
+:::
+
+A `url` that ends in `*` is a catch-all route. It matches any remaining path, which is useful for showing a "not found" page. The part of the path matched by `*` is passed to the component as the `pathMatch` property, without a leading `/`.
+
+```html
+
+
+
+
+
+
+
+```
+
+With these routes:
+
+| Path | Renders | `pathMatch` |
+| --------------------- | ---------------------- | -------------- |
+| `/admin` | `page-admin` | |
+| `/admin/users/1` | `page-admin-user` | |
+| `/admin/settings` | `page-admin-not-found` | `settings` |
+| `/admin/users/1/edit` | `page-admin-not-found` | `users/1/edit` |
+| `/settings` | `page-not-found` | `settings` |
+
+The component reads `pathMatch` like any other property:
+
+
+
+
+
+```javascript
+customElements.define(
+ 'page-not-found',
+ class extends HTMLElement {
+ connectedCallback() {
+ this.innerHTML = `
+
+
+
+ );
+ }
+}
+```
+
+
+
+
+
+### Matching Priority {/* #matching-priority */}
+
+Without a catch-all, a route that has a `url` and no child routes also matches paths that have extra segments after its `url`. Routes with an empty `url`, such as `/` or a default child route, only match exactly. When a catch-all route matches the path, routes that only match by ignoring extra segments are no longer considered, even if another route wins.
+
+When a catch-all route and a route without a catch-all both match the whole path, the route with more fixed segments wins. A fixed segment is one that is not a parameter, such as `admin`. If they have the same number, the route without the catch-all wins.
+
+Using the routes from the example above:
+
+| Path | Without catch-alls | With catch-alls | Why |
+| --------------------- | ------------------ | ---------------------- | --------------------------------------------------------------------------------------------- |
+| `/admin` | `page-admin` | `page-admin` | `/admin` and `/admin/*` both have one fixed segment, so the route without the catch-all wins. |
+| `/admin/users/1` | `page-admin-user` | `page-admin-user` | `/admin/users/:id` has more fixed segments than `/admin/*`. |
+| `/admin/settings` | `page-admin` | `page-admin-not-found` | `page-admin` only matched by ignoring `settings`, so the catch-all wins. |
+| `/admin/users/1/edit` | `page-admin-user` | `page-admin-not-found` | `page-admin-user` only matched by ignoring `edit`, so the catch-all wins. |
+| `/settings` | No match | `page-not-found` | No other route matches. |
+
+When more than one catch-all route matches, the one with the most fixed segments wins. Segments from parent routes count toward this total, so a catch-all route nested inside another route can handle unknown paths below it.
+
+Redirects run before routes are matched. A redirect from `*` applies to every path, so `` takes precedence over ``.
+
+### Caveats {/* #caveats */}
+
+A catch-all route also matches its own prefix, so `/admin/*` matches `/admin` with `pathMatch` set to an empty string. This differs from [`ion-route-redirect`](route-redirect.mdx), where `from="/admin/*"` does not match `/admin`.
+
+A `*` is only treated as a wildcard when it is the last segment of the `url`. On a route with child routes, the `*` is ignored and the route matches as if its `url` did not end in `*`.
+
+The router sets `pathMatch` on catch-all routes and overwrites any value passed in `componentProps`, so do not pass it there.
+
+If the `component` of a catch-all route is a tab in `ion-tabs`, the tab does not receive `pathMatch`. Instead, nest the catch-all route as a child of the tab's route so it renders in that tab's `ion-router-outlet`:
+
+```html
+
+
+
+
+```
+
## Navigation Hooks {/* #navigation-hooks */}
Navigation hooks can be used to perform tasks or act as navigation guards. Hooks are used by providing functions to the `beforeEnter` and `beforeLeave` properties on each `ion-route`. Returning `true` allows navigation to proceed, while returning `false` causes it to be cancelled. Returning an object of type `NavigationHookOptions` allows you to redirect navigation to another page.
diff --git a/docs/api/router.mdx b/docs/api/router.mdx
index d9cc1cd60d..1b5262397d 100644
--- a/docs/api/router.mdx
+++ b/docs/api/router.mdx
@@ -77,8 +77,12 @@ interface RouterCustomEvent extends CustomEvent {
## Usage {/* #usage */}
+The last route is a [catch-all route](route.mdx#catch-all-routes) that renders `page-not-found` for any URL that no other route matches.
+
```html
+
+
@@ -100,6 +104,7 @@ interface RouterCustomEvent extends CustomEvent {
+
```