matchPath - greydragon888/real-router GitHub Wiki
getPluginApi().matchPath
1. Overview
- What it does: Matches a URL path against the route tree and returns a
Stateobject - When to use:
- Popstate event with no
history.state— plugin needs to determine the route from the URL - Deep link handling — resolve a URL to a route state
- SSR initial state — match the incoming request URL to a route
- Popstate event with no
- Access:
getPluginApi(router).matchPath(path)— this is a plugin API method, not a router method
2. Signature
import { getPluginApi } from "@real-router/core/api";
const pluginApi = getPluginApi(router);
pluginApi.matchPath<P extends Params = Params>(
path: string
): State<P> | undefined
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
path |
string |
Yes | URL path to match (e.g., /users/123?tab=profile) |
Return Value
- Match found: Frozen
Stateobject with route information - No match:
undefined
// Returned State structure
{
name: "users.profile", // Route name
params: { id: "123" }, // Decoded parameters
path: "/users/123", // URL path (possibly rewritten)
}
// Segment parameter metadata is stored internally (WeakMap), not on the State object
3. Possible Errors
| Condition | Error |
|---|---|
path not a string |
TypeError: [real-router] matchPath: path must be a string, got {type} |
| Decoder throws | Error propagated (not suppressed) |
Validation requires @real-router/validation-plugin. Without the plugin, no argument validation is performed.
matchPath does not check disposed state — it works even after router.dispose().
4. Related Methods
| Method | Description |
|---|---|
buildState |
Validate route and get segment metadata |
forwardState |
Resolve forwarding and merge params |
buildPath |
Reverse operation — route name to URL |
buildNavigationState |
Build full State without navigation |
5. Behavior
Route Decoders
If the matched route has decodeParams, it is applied to the matched parameters:
import { getPluginApi, getRoutesApi } from "@real-router/core/api";
const api = getPluginApi(router);
const routesApi = getRoutesApi(router);
routesApi.add({
name: "user",
path: "/user/:id",
decodeParams: (p) => ({ id: Number.parseInt(p.id, 10) }),
});
const state = api.matchPath("/user/123");
// state.params.id === 123 (number, not string)
URL Decoding
Parameters are automatically URL-decoded:
const api = getPluginApi(router);
api.matchPath("/user/%E6%97%A5%E6%9C%AC%E8%AA%9E");
// state.params.name === "日本語"
Forwarding
If the matched route has forwardTo, the returned state reflects the target route. Forwarding is resolved through the forwardState function on the router internals, so plugins that override forwardState (e.g., persistent-params-plugin) can intercept during path matching.
import { getPluginApi, getRoutesApi } from "@real-router/core/api";
const api = getPluginApi(router);
const routesApi = getRoutesApi(router);
routesApi.update("old-users", { forwardTo: "users" });
const state = api.matchPath("/old-users/123");
// state.name === "users" (forwarded)
Path Normalization
When rewritePathOnMatch: true (default), the returned state.path is rebuilt via buildPath, applying forwardTo aliases, encoders, defaultParams, and trailing-slash normalization:
const api = getPluginApi(router);
const state = api.matchPath("/user/123/");
// With trailingSlash: "never" → state.path === "/user/123"
// With trailingSlash: "always" → state.path === "/user/123/"
// With trailingSlash: "preserve" (default) → state.path === "/user/123/" — the source path's trailing-slash wins
The "preserve" case is honoured even though the matcher builds the canonical path without a trailing slash: the source path's trailing-slash choice is re-attached afterwards. See RouterOptions — trailingSlash.
Router Options That Affect Matching
| Option | Effect |
|---|---|
caseSensitive |
Whether /Users matches route /users |
trailingSlash |
How trailing slashes are handled |
queryParamsMode |
"default" accepts any params, "strict" only declared |
rewritePathOnMatch |
Whether state.path is rebuilt from the matched route |
6. Migration
The matchPath method moved from the router instance to the plugin API:
// Before
const state = router.matchPath(path);
// After
import { getPluginApi } from "@real-router/core/api";
const api = getPluginApi(router);
const state = api.matchPath(path);
Plugin Example
import { getPluginApi } from "@real-router/core/api";
import type { PluginFactory } from "@real-router/core";
const myBrowserPlugin: PluginFactory = (router) => {
const api = getPluginApi(router);
async function onExternalUrlChange(url: string) {
const path = extractPath(url);
const state = api.matchPath(path);
if (!state) {
router.navigateToDefault({ replace: true });
return;
}
try {
await api.navigateToState(state, router.getState(), { replace: true });
} catch {
// Handle navigation error
}
}
return {
onStart() {
window.addEventListener("customnavigation", onExternalUrlChange);
},
teardown() {
window.removeEventListener("customnavigation", onExternalUrlChange);
},
};
};