API
Config
Contains all page logic
Configurable properties
ts | Route path, must start with / and may include dynamic segments |
ts | Static props passed to the page component |
ts | Validators for dynamic path segments |
ts | Validators for `query` parameters |
ts | Function that returns a Promise with the component in the default property |
ts | Lifecycle function called before redirecting to the page |
ts | Lifecycle function called before leaving the page |
System properties
ts | Corresponds to the object key |
ts | The default field returned by loader |
ts | All exports returned by |
config.params
In Reactive Route, there is no explicit split between static and dynamic Config; path segments prefixed with : are controlled by validators that allow a page to open with a value from the URL, and this is reflected in the typings.
users: {
path: '/users', // no validators
loader: () => import('./pages/users')
},
userDetails: {
path: '/user/:id', // "id" is a param matched by the validator
params: {
id: (value) => /^\d+$/.test(value)
},
loader: () => import('./pages/userDetails')
}Config typing tests
import { createConfigs } from '../../packages/core';
const loader = async () => ({ default: null });
const v = () => true;
createConfigs({
dynamic: { path: '/:id/:tab', params: { id: v, tab: v }, loader },
static: { path: '/', loader },
notFound: { path: '/404', props: {}, loader },
internalError: { path: '/500', props: {}, loader },
// @ts-expect-error "params" must be "never"
static1: { path: '/', loader, params: {} },
// @ts-expect-error "params" are required
dynamic1: { path: '/:id/:tab', loader },
// @ts-expect-error "params.tab" and "params.id" are required
dynamic2: { path: '/:id/:tab', params: {}, loader },
// @ts-expect-error "params.tab" is required
dynamic3: { path: '/:id/:tab', params: { id: v }, loader },
// @ts-expect-error "params" validators must be functions
dynamic4: { path: '/:id/:tab', params: { id: v, tab: '' }, loader },
// @ts-expect-error duplicates are not allowed
double1: { path: '/:id/:id', params: { id: v }, loader },
// @ts-expect-error duplicates are not allowed
double2: { path: '/:id/:id', loader },
// @ts-expect-error duplicates are not allowed
double3: { path: '/:id/:id/', params: { id: v }, loader },
// @ts-expect-error duplicates are not allowed
double4: { path: '/:id/:id/', loader },
// @ts-expect-error duplicates are not allowed
double5: { path: '/:id/:tab/:id', params: { id: v, tab: v }, loader },
// @ts-expect-error duplicates are not allowed
double6: { path: '/:id/:tab/:id', loader },
});
// @ts-expect-error internalError is required
createConfigs({ notFound: { path: '/404', loader } });
// @ts-expect-error notFound is required
createConfigs({ internalError: { path: '/500', loader } });
createConfigs({
// @ts-expect-error "query" is not allowed
notFound: { path: '/404', loader, query: {} },
// @ts-expect-error "params" are not allowed
internalError: { path: '/500', loader, params: { code: v } },
});
createConfigs({
// @ts-expect-error "beforeLeave" is not allowed
notFound: { path: '/404', loader, beforeLeave: () => undefined },
// @ts-expect-error "beforeEnter" is not allowed
internalError: { path: '/500', loader, beforeEnter: () => undefined },
});Important
Validators receive decoded values for convenience when working with non-English URLs:
id: (value) => console.log(value) will print not with%20space, but with space
This way, Reactive Route guarantees that all parameters in router.state.userDetails.params have passed validation and can be used safely.
At runtime, path is always the source of truth, and stable matching is guaranteed even with TS disabled. The declaration order of Config does not matter — the router algorithm has only one rule: when the URL fully matches path, that config has the highest priority, even if it is declared last. In all other scenarios, the first Config whose validators all pass wins.
config.query
They are described in the same format as params, as validators:
search: {
path: '/search',
query: {
userPrompt: (value) => value.length > 5
},
loader: () => import('./pages/search')
}Important
Validators receive decoded values:
userPrompt: (value) => console.log(value) will print not with%20space, but with space
All query parameters are optional, and their absence does not cause a redirect to notFound.
const pageState = router.state.search;
await router.redirect({ name: 'search', query: { userPrompt: 'short' }})
console.log(pageState.query.userPrompt) // undefined
await router.redirect({ name: 'search', query: { userPrompt: 'enough' }})
console.log(pageState.query.userPrompt) // 'enough'If you need certain query parameters to be present, you can check them in beforeEnter and redirect to notFound if they are missing from nextState.query.
config.beforeEnter
This async function can be used to redirect to another Config, perform auth checks, and load data. Unhandled errors will lead to rendering internalError without changing the URL in the browser.
Arguments
ts | Reason why beforeEnter was called. If both params and query changed, then new_params |
ts | Next expected State |
ts | Current active State (undefined on the very first redirect) |
ts | Method for redirecting inside the lifecycle. Since createConfigs is called before the router is created, router.redirect cannot be used here |
dashboard: {
path: '/dashboard',
loader: () => import('./pages/dashboard'),
async beforeEnter({ redirect }) {
await api.loadUser();
if (!store.isAuthenticated()) {
return redirect({ name: 'login', query: { returnTo: 'dashboard' } });
}
await api.loadDashboard();
}
}beforeEnter / beforeLeave typing tests
import { createConfigs } from '../../packages/core';
const loader = async () => ({ default: null });
createConfigs({
static: {
path: '/',
loader,
async beforeEnter({ currentState, nextState, reason, redirect }) {
currentState?.name;
currentState?.params;
currentState?.query;
// @ts-expect-error unknown "currentState" property is not available
currentState?.unknown;
nextState.name;
nextState.params;
nextState.query;
// @ts-expect-error unknown "nextState" property is not available
nextState.unknown;
reason satisfies 'unmodified' | 'new_query' | 'new_params' | 'new_config';
// @ts-expect-error unknown reason is not assignable
reason satisfies 'unknown';
redirect({ name: 'static' });
redirect({ name: 'static', replace: true });
redirect({ name: 'unknown' });
// @ts-expect-error "replace" must be boolean
redirect({ name: 'static', replace: 'true' });
// @ts-expect-error redirect params values must be strings
redirect({ name: 'dynamic', params: { id: 1 } });
},
async beforeLeave({ currentState, nextState, reason, preventRedirect }) {
currentState.name;
currentState.params;
currentState.query;
// @ts-expect-error unknown "currentState" property is not available
currentState.unknown;
nextState.name;
nextState.params;
nextState.query;
// @ts-expect-error unknown "nextState" property is not available
nextState.unknown;
reason satisfies 'unmodified' | 'new_query' | 'new_params' | 'new_config';
// @ts-expect-error unknown reason is not assignable
reason satisfies 'unknown';
preventRedirect();
// @ts-expect-error "preventRedirect" does not accept arguments
preventRedirect(true);
},
},
notFound: { path: '/404', loader },
internalError: { path: '/500', loader },
});Limitations
Only in lifecycle functions do redirect, currentState, and nextState have incomplete typings (name is just string) due to TypeScript 5 limitations, so TS will not report errors during refactoring.
TIP
Always use return with redirect and preventRedirect for stable redirect logic.
config.beforeLeave
This async function can be used to interrupt a redirect. Unhandled errors will lead to rendering internalError without changing the URL in the browser.
Arguments
ts | Reason why beforeLeave was called. If both params and query changed, then new_params |
ts | Next expected State |
ts | Current active State |
ts | Method for stopping the redirect |
dashboard: {
path: '/dashboard',
loader: () => import('./pages/dashboard'),
async beforeLeave({ preventRedirect, nextState }) {
if (nextState.name === 'login') return preventRedirect();
// Do not check for unsaved changes on the server
if (typeof window === 'undefined') return;
const hasUnsavedChanges = await api.checkForm();
if (hasUnsavedChanges && !window.confirm(
`You have unsaved changes. Are you sure you want to leave?`
)) return preventRedirect();
}
}beforeEnter / beforeLeave typing tests
import { createConfigs } from '../../packages/core';
const loader = async () => ({ default: null });
createConfigs({
static: {
path: '/',
loader,
async beforeEnter({ currentState, nextState, reason, redirect }) {
currentState?.name;
currentState?.params;
currentState?.query;
// @ts-expect-error unknown "currentState" property is not available
currentState?.unknown;
nextState.name;
nextState.params;
nextState.query;
// @ts-expect-error unknown "nextState" property is not available
nextState.unknown;
reason satisfies 'unmodified' | 'new_query' | 'new_params' | 'new_config';
// @ts-expect-error unknown reason is not assignable
reason satisfies 'unknown';
redirect({ name: 'static' });
redirect({ name: 'static', replace: true });
redirect({ name: 'unknown' });
// @ts-expect-error "replace" must be boolean
redirect({ name: 'static', replace: 'true' });
// @ts-expect-error redirect params values must be strings
redirect({ name: 'dynamic', params: { id: 1 } });
},
async beforeLeave({ currentState, nextState, reason, preventRedirect }) {
currentState.name;
currentState.params;
currentState.query;
// @ts-expect-error unknown "currentState" property is not available
currentState.unknown;
nextState.name;
nextState.params;
nextState.query;
// @ts-expect-error unknown "nextState" property is not available
nextState.unknown;
reason satisfies 'unmodified' | 'new_query' | 'new_params' | 'new_config';
// @ts-expect-error unknown reason is not assignable
reason satisfies 'unknown';
preventRedirect();
// @ts-expect-error "preventRedirect" does not accept arguments
preventRedirect(true);
},
},
notFound: { path: '/404', loader },
internalError: { path: '/500', loader },
});Limitations
Only in lifecycle functions do redirect, currentState, and nextState have incomplete typings (name is just string) due to TypeScript 5 limitations, so TS will not report errors during refactoring.
TIP
Always use return with redirect and preventRedirect for stable redirect logic.
State
A reactive object stored in router.state.
Properties
ts | Corresponds to the Config key |
ts | Validated and decoded `params` values. All of them will always be present |
ts | Validated and decoded `query` values. All are optional |
State typing tests
import { createConfigs, createRouter } from '../../packages/core';
const loader = async () => ({ default: null });
const v: (value: string) => boolean = () => true;
const adapters = {} as any;
const configs = createConfigs({
static: { path: '/', loader },
staticQuery: { path: '/', query: { q: v }, loader },
dynamic: { path: '/:id', params: { id: v }, loader },
dynamicQuery: {
path: '/:id',
params: { id: v },
query: { q: v },
loader,
},
notFound: { path: '/404', loader },
internalError: { path: '/500', loader },
});
const router = createRouter({ configs, adapters });
// @ts-expect-error not existing name
router.state.unknown;
router.state.static!.name;
router.state.static!.params;
router.state.static!.query;
// @ts-expect-error "params" values are not available
router.state.static!.params.unknown;
// @ts-expect-error "query" values are not available
router.state.static!.query.unknown;
router.state.notFound!.name;
router.state.notFound!.params;
// @ts-expect-error "params" values are not available
router.state.notFound!.params.unknown;
router.state.notFound!.query;
// @ts-expect-error "query" values are not available
router.state.notFound!.query.unknown;
router.state.internalError!.name;
router.state.internalError!.params;
// @ts-expect-error "params" values are not available
router.state.internalError!.params.unknown;
router.state.internalError!.query;
// @ts-expect-error "query" values are not available
router.state.internalError!.query.unknown;
router.state.staticQuery!.name;
router.state.staticQuery!.params;
// @ts-expect-error "params" values are not available
router.state.staticQuery!.params.unknown;
router.state.staticQuery!.query.q;
// @ts-expect-error unknown "query" are not available
router.state.staticQuery!.query.unknown;
router.state.dynamic!.name;
router.state.dynamic!.params.id;
// @ts-expect-error unknown "params" are not available
router.state.dynamic!.params.unknown;
router.state.dynamic!.query;
// @ts-expect-error "query" values are not available
router.state.dynamic!.query.unknown;
router.state.dynamicQuery!.name;
router.state.dynamicQuery!.params.id;
router.state.dynamicQuery!.query.q;
// @ts-expect-error unknown "params" are not available
router.state.dynamicQuery!.params.unknown;
// @ts-expect-error unknown "query" are not available
router.state.dynamicQuery!.query.unknown;Type Narrowing for State
import { createConfigs, createRouter } from '../../packages/core';
const loader = async () => ({ default: null });
const v: (value: string) => boolean = () => true;
const adapters = {} as any;
const configs = createConfigs({
static: { path: '/', loader },
staticQuery: { path: '/', query: { q: v }, loader },
dynamic: { path: '/:id', params: { id: v }, loader },
dynamicQuery: {
path: '/:id',
params: { id: v },
query: { q: v },
loader,
},
notFound: { path: '/404', loader },
internalError: { path: '/500', loader },
});
const router = createRouter({ configs, adapters });
const state = router.urlToState('');
// @ts-expect-error not existing name
state.name === 'unknown';
if (state.name === 'notFound') {
state.name;
state.params;
// @ts-expect-error "params" values are not available
state.params.unknown;
state.query;
// @ts-expect-error "query" values are not available
state.query.unknown;
}
if (state.name === 'internalError') {
state.name;
state.params;
// @ts-expect-error "params" values are not available
state.params.unknown;
state.query;
// @ts-expect-error "query" values are not available
state.query.unknown;
}
if (state.name === 'static') {
state.name;
state.params;
state.query;
// @ts-expect-error "params" values are not available
state.params.unknown;
// @ts-expect-error "query" values are not available
state.query.unknown;
}
if (state.name === 'staticQuery') {
state.name;
state.params;
// @ts-expect-error "params" values are not available
state.params.unknown;
state.query.q;
// @ts-expect-error unknown "query" are not available
state.query.unknown;
}
if (state.name === 'dynamic') {
state.name;
state.params.id;
// @ts-expect-error unknown "params" are not available
state.params.unknown;
state.query;
// @ts-expect-error "query" values are not available
state.query.unknown;
}
if (state.name === 'dynamicQuery') {
state.name;
state.params.id;
state.query.q;
// @ts-expect-error unknown "params" are not available
state.params.unknown;
// @ts-expect-error unknown "query" are not available
state.query.unknown;
}Router
createRouter
This function creates router.
Arguments
ts | Object with Configs |
ts | Adapters for the reactivity system |
ts | Global lifecycle function that runs only when the component changes (not the page!) |
beforeComponentChange
This function is called only when the rendered component changes and is intended for use in modular architectures.
// Some page exports "export class PageStore { data: {}, destroy() {} }"
const globalStore = { pages: {} };
createRouter({
configs,
adapters,
beforeComponentChange({ prevConfig, currentConfig }) {
// PageStore is accessible from the otherExports object
const { PageStore } = currentConfig.otherExports;
if (PageStore) globalStore.pages[currentConfig.name] = new PageStore();
// destroy the obsolete PageStore from the previuos page
globalStore.pages[prevConfig.name]?.destroy();
}
})beforeComponentChange typing tests
import { createConfigs, createRouter } from '../../packages/core';
const loader = async () => ({ default: null });
const v: (value: string) => boolean = () => true;
const adapters = {} as any;
const configs = createConfigs({
static: { path: '/', loader },
staticQuery: { path: '/', query: { q: v }, loader },
dynamic: { path: '/:id', params: { id: v }, loader },
dynamicQuery: {
path: '/:id',
params: { id: v },
query: { q: v },
loader,
},
notFound: { path: '/404', loader },
internalError: { path: '/500', loader },
});
createRouter({
configs,
adapters,
beforeComponentChange({ prevConfig, prevState, currentConfig, currentState }) {
/** prevConfig */
prevConfig?.name;
prevConfig?.loader;
// @ts-expect-error unknown prevConfig property is not available
prevConfig?.unknown;
if (prevConfig?.name === 'dynamic') {
prevConfig.params.id;
// @ts-expect-error unknown "params" are not available
prevConfig.params.unknown;
// @ts-expect-error "query" is not available
prevConfig.query.q;
}
if (prevConfig?.name === 'static') {
// @ts-expect-error "params" is not available
prevConfig.params.id;
// @ts-expect-error "query" is not available
prevConfig.query.q;
}
if (prevConfig?.name === 'staticQuery') {
prevConfig.query.q;
// @ts-expect-error unknown "query" are not available
prevConfig.query.unknown;
// @ts-expect-error "params" is not available
prevConfig.params.id;
}
if (prevConfig?.name === 'dynamicQuery') {
prevConfig.params.id;
prevConfig.query.q;
// @ts-expect-error unknown "params" are not available
prevConfig.params.unknown;
// @ts-expect-error unknown "query" are not available
prevConfig.query.unknown;
}
if (prevConfig?.name === 'notFound') {
// @ts-expect-error "params" is not available
prevConfig.params.code;
// @ts-expect-error "query" is not available
prevConfig.query.q;
}
if (prevConfig?.name === 'internalError') {
// @ts-expect-error "params" is not available
prevConfig.params.code;
// @ts-expect-error "query" is not available
prevConfig.query.q;
}
/** currentConfig */
currentConfig.name;
currentConfig.loader;
// @ts-expect-error unknown currentConfig property is not available
currentConfig.unknown;
if (currentConfig.name === 'notFound') {
// @ts-expect-error "params" is not available
currentConfig.params.code;
// @ts-expect-error "query" is not available
currentConfig.query.q;
}
if (currentConfig.name === 'internalError') {
// @ts-expect-error "params" is not available
currentConfig.params.code;
// @ts-expect-error "query" is not available
currentConfig.query.q;
}
if (currentConfig.name === 'static') {
// @ts-expect-error "params" is not available
currentConfig.params.id;
// @ts-expect-error "query" is not available
currentConfig.query.q;
}
if (currentConfig.name === 'staticQuery') {
currentConfig.query.q;
// @ts-expect-error unknown "query" are not available
currentConfig.query.unknown;
// @ts-expect-error "params" is not available
currentConfig.params.id;
}
if (currentConfig.name === 'dynamicQuery') {
currentConfig.params.id;
currentConfig.query.q;
// @ts-expect-error unknown "params" are not available
currentConfig.params.unknown;
// @ts-expect-error unknown "query" are not available
currentConfig.query.unknown;
}
if (currentConfig.name === 'dynamic') {
currentConfig.params.id;
// @ts-expect-error unknown "params" are not available
currentConfig.params.unknown;
// @ts-expect-error "query" is not available
currentConfig.query.q;
}
/** prevState */
prevState?.name;
prevState?.params;
prevState?.query;
// @ts-expect-error unknown prevState property is not available
prevState?.unknown;
// @ts-expect-error not existing name
prevState?.name === 'unknown';
if (prevState?.name === 'staticQuery') {
prevState.query.q;
// @ts-expect-error unknown "query" are not available
prevState.query.unknown;
// @ts-expect-error "params" values are not available
prevState.params.unknown;
}
if (prevState?.name === 'notFound') {
prevState.params;
// @ts-expect-error "params" values are not available
prevState.params.unknown;
prevState.query;
// @ts-expect-error "query" values are not available
prevState.query.unknown;
}
if (prevState?.name === 'internalError') {
prevState.params;
// @ts-expect-error "params" values are not available
prevState.params.unknown;
prevState.query;
// @ts-expect-error "query" values are not available
prevState.query.unknown;
}
if (prevState?.name === 'static') {
prevState.params;
prevState.query;
// @ts-expect-error "params" values are not available
prevState.params.unknown;
// @ts-expect-error "query" values are not available
prevState.query.unknown;
}
if (prevState?.name === 'dynamic') {
prevState.params.id;
// @ts-expect-error unknown "params" are not available
prevState.params.unknown;
prevState.query;
// @ts-expect-error "query" values are not available
prevState.query.unknown;
}
if (prevState?.name === 'dynamicQuery') {
prevState.params.id;
prevState.query.q;
// @ts-expect-error unknown "params" are not available
prevState.params.unknown;
// @ts-expect-error unknown "query" are not available
prevState.query.unknown;
}
/** currentState */
currentState.name;
currentState.params;
currentState.query;
// @ts-expect-error unknown currentState property is not available
currentState.unknown;
// @ts-expect-error not existing name
currentState.name === 'unknown';
if (currentState.name === 'notFound') {
currentState.params;
// @ts-expect-error "params" values are not available
currentState.params.unknown;
currentState.query;
// @ts-expect-error "query" values are not available
currentState.query.unknown;
}
if (currentState.name === 'internalError') {
currentState.params;
// @ts-expect-error "params" values are not available
currentState.params.unknown;
currentState.query;
// @ts-expect-error "query" values are not available
currentState.query.unknown;
}
if (currentState.name === 'static') {
currentState.params;
currentState.query;
// @ts-expect-error "params" values are not available
currentState.params.unknown;
// @ts-expect-error "query" values are not available
currentState.query.unknown;
}
if (currentState.name === 'staticQuery') {
currentState.query.q;
// @ts-expect-error unknown "query" are not available
currentState.query.unknown;
// @ts-expect-error "params" values are not available
currentState.params.unknown;
}
if (currentState.name === 'dynamic') {
currentState.params.id;
// @ts-expect-error unknown "params" are not available
currentState.params.unknown;
// @ts-expect-error "query" values are not available
currentState.query.unknown;
}
if (currentState.name === 'dynamicQuery') {
currentState.params.id;
currentState.query.q;
// @ts-expect-error unknown "params" are not available
currentState.params.unknown;
// @ts-expect-error unknown "query" are not available
currentState.query.unknown;
}
},
});This way, the user page can access its PageStore through globalStore.pages.user. This makes it possible to use code-splitting more efficiently and serialize only globalStore during SSR — it will already contain the data for the required page.
This function can also be used to interrupt async operations and subscriptions.
router.redirect
Performs the full redirect cycle, described in more detail in the how it works section. If you pass an additional replace: true property, the last browser history entry will be replaced. Returns a string with the new URL.
The second argument is skipLifecycle?: boolean if you need to skip beforeEnter and beforeLeave calls.
const newUrl = await router.redirect({
name: 'user',
params: { id: '9999' },
query: { phone: '123456' }
})
// '/user/9999?phone=123456'StateDynamic typing tests
import { createConfigs, createRouter } from '../../packages/core';
const loader = async () => ({ default: null });
const v: (value: string) => boolean = () => true;
const adapters = {} as any;
const configs = createConfigs({
static: { path: '/', loader },
staticQuery: { path: '/', query: { q: v }, loader },
dynamic: { path: '/:id', params: { id: v }, loader },
dynamicQuery: {
path: '/:id',
params: { id: v },
query: { q: v },
loader,
},
notFound: { path: '/404', loader },
internalError: { path: '/500', loader },
});
const router = createRouter({ configs, adapters });
await router.redirect({ name: 'static' });
await router.redirect({ name: 'staticQuery' });
await router.redirect({ name: 'staticQuery', query: { q: '' } });
await router.redirect({ name: 'dynamic', params: { id: '' } });
await router.redirect({ name: 'dynamicQuery', params: { id: '' } });
await router.redirect({
name: 'dynamicQuery',
params: { id: '' },
query: { q: '' },
});
/** Not string values raise errors */
await router.redirect({
// @ts-expect-error name must be a string
name: 1,
});
await router.redirect({
name: 'staticQuery',
// @ts-expect-error "query" values must be strings
query: { q: 1 },
});
await router.redirect({
name: 'dynamic',
// @ts-expect-error "params" values must be strings
params: { id: 1 },
});
await router.redirect({
name: 'dynamicQuery',
params: { id: '' },
// @ts-expect-error "query" values must be strings
query: { q: 1 },
});
// @ts-expect-error "params" are required
await router.redirect({ name: 'dynamic' });
await router.redirect({
name: 'dynamic',
// @ts-expect-error "params" are incomplete
params: {},
});
/** Irrelevant StateDynamic raise errors */
// @ts-expect-error name must be present
await router.redirect({});
// @ts-expect-error "name" must be a string key of configs
await router.redirect({ name: 'unknown' });
await router.redirect({
name: 'static',
// @ts-expect-error "params" are not allowed
params: { id: '' },
});
await router.redirect({
name: 'staticQuery',
// @ts-expect-error "params" are not allowed
params: { id: '' },
});
await router.redirect({
name: 'dynamic',
// @ts-expect-error extra "params" are not allowed
params: { id: '', extra: '' },
});
await router.redirect({
name: 'static',
// @ts-expect-error "query" is not allowed
query: { q: '' },
});
await router.redirect({
name: 'dynamic',
params: { id: '' },
// @ts-expect-error "query" is not allowed
query: { q: '' },
});
await router.redirect({
name: 'staticQuery',
// @ts-expect-error unknown "query" key
query: { unknown: '' },
});
await router.redirect({
name: 'dynamicQuery',
params: { id: '' },
// @ts-expect-error unknown "query" key
query: { unknown: '' },
});
const state = router.urlToState('');
void router.redirect(state);router.urlToState
Accepts a URL and returns State with a fallback to notFound.
router.urlToState(`/user/9999?phone=123456>m=value`);
// {
// name: 'user',
// params: { id: '9999' },
// query: { phone: '123456' }
// }
router.urlToState(`/not-existing/admin?hacker=sql-inject`);
// {
// name: 'notFound',
// params: {},
// query: {}
// }INFO
Only the described query values that pass validation are preserved; in this case, gtm does not end up in State.
router.init
Short form of router.redirect(router.urlToState(url)). The second argument is skipLifecycle?: boolean if you need to skip beforeEnter and beforeLeave calls.
// browser usage example:
await router.init(location.href)
// browser usage with SSR example:
await router.init(location.href, { skipLifecycle: true })
// Express.js server usage example:
const newUrl = await router.init(req.originalUrl)
// Optional step if you want to clear irrelevant query
if (req.originalUrl !== newUrl) res.redirect(newUrl)router.state
Reactive object whose keys are name and whose values are State, for example:
console.log(router.state.user);
// reactive object
// {
// name: 'user',
// params: { id: '9999' },
// query: { phone: '123456' }
// }Intended for displaying values in the UI and for describing logic in autoruns/effects. On redirect with new params or query, these values will change accordingly in router.state.user.
The router does not destroy the old State when navigating to another Config. In this example, if you navigate to router.redirect({ name: 'home' }), router.state.user will still be present. This helps solve the problem of uncleared subscriptions to old state at runtime.
If Reactive Route stored only one active router.getActiveState() (this method does not exist!), like many non-reactive routers do, then the subscription would start before component unmount with an incorrect State in which these parameters might be missing.
// with mobx adapter
import { autorun } from 'mobx'
function PageUser() {
const { router } = useContext(RouterContext);
const pageState = router.state.user!;
useEffect(() => autorun(() => {
console.log(pageState.params.id, pageState.query.phone);
}), []);
}// with kr-observable adapter
import { autorun } from 'kr-observable'
function PageUser() {
const { router } = useContext(RouterContext);
const pageState = router.state.user!;
useEffect(() => autorun(() => {
console.log(pageState.params.id, pageState.query.phone);
}), []);
}// with solid adapter
import { createRenderEffect } from 'solid-js'
function PageUser() {
const { router } = useContext(RouterContext);
const pageState = router.state.user!;
createRenderEffect(() => {
console.log(pageState.params.id, pageState.query.phone);
})
}<script lang="ts" setup>
// with vue adapter
import { watchEffect } from 'vue';
const { router } = useRouterStore();
const pageState = router.state.user!;
watchEffect(() => {
console.log(pageState.params.id, pageState.query.phone);
});
</script>router.isRedirecting
Reactive boolean for displaying loading indicators during redirects. Examples of global and local display are shown below:
function GlobalLoader() {
const { router } = useContext(RouterContext);
return router.isRedirecting ? <Loader/> : null;
}
function SomeComponent() {
const { router } = useContext(RouterContext);
return <Button isLoading={router.isRedirecting}>Send</Button>;
}function GlobalLoader() {
const { router } = useContext(RouterContext);
return router.isRedirecting ? <Loader/> : null;
}
function SomeComponent() {
const { router } = useContext(RouterContext);
return <Button isLoading={router.isRedirecting}>Send</Button>;
}function GlobalLoader() {
const { router } = useContext(RouterContext);
return <Show when={router.isRedirecting}><Loader/></Show>;
}
function SomeComponent() {
const { router } = useContext(RouterContext);
return <Button isLoading={router.isRedirecting}>Send</Button>;
}<script lang="ts" setup>
import { useRouter } from '../../router';
const { router } = useRouter();
</script>
<template>
<Loader v-if="router.isRedirecting"/>
<Button :is-loading="router.isRedirecting">Send</Button>
</template>router.activeName
Reactive name of the active State (undefined until the very first redirect).
router.preloadComponent
Reactive Route loads page chunks (executes loader) only during redirects. This function can be used for preloading and accepts name
await router.init(location.href);
setTimeout(async () => {
try {
await router.preloadComponent('login')
await router.preloadComponent('dashboard')
} catch (e) {
console.error('Seems like the user lost connection')
}
}, 5000)router.getGlobalArguments
Allows you to get the configuration passed to createRouter.