@real-router/core
v0.40.1
Published
A simple, powerful, view-agnostic, modular and extensible router
Downloads
2,807
Maintainers
Readme
@real-router/core
Simple, powerful, view-agnostic, modular and extensible router for JavaScript applications.
This is the core package of the Real-Router monorepo. It provides the router implementation, lifecycle management, navigation pipeline, and tree-shakeable standalone API modules.
Installation
npm install @real-router/coreQuick Start
import { createRouter } from "@real-router/core";
import { browserPluginFactory } from "@real-router/browser-plugin";
const routes = [
{ name: "home", path: "/" },
{
name: "users",
path: "/users",
children: [{ name: "profile", path: "/:id" }],
},
];
const router = createRouter(routes);
router.usePlugin(browserPluginFactory());
await router.start("/");
await router.navigate("users.profile", { id: "123" });Router API
Lifecycle
| Method | Returns | Description |
| ------------- | ---------------- | ---------------------------------------------- |
| start(path) | Promise<State> | Start the router with an initial path |
| stop() | this | Stop the router, cancel in-progress transition |
| dispose() | void | Permanently terminate (cannot restart) |
| isActive() | boolean | Whether the router is started |
Navigation
| Method | Returns | Description |
| ----------------------------------- | ---------------- | ----------------------------------------- |
| navigate(name, params?, options?) | Promise<State> | Navigate to a route. Fire-and-forget safe |
| navigateToDefault(options?) | Promise<State> | Navigate to the default route |
| navigateToNotFound(path?) | State | Synchronously set UNKNOWN_ROUTE state |
| canNavigateTo(name, params?) | boolean | Check if guards allow navigation |
await router.navigate("users.profile", { id: "123" });
await router.navigate("dashboard", {}, { replace: true });
// Cancellable navigation
const controller = new AbortController();
router.navigate("users", {}, { signal: controller.signal });
controller.abort();State
| Method | Returns | Description |
| -------------------------------------------------- | -------------------- | ------------------------------------ |
| getState() | State \| undefined | Current router state (deeply frozen) |
| getPreviousState() | State \| undefined | Previous router state |
| areStatesEqual(s1, s2, ignoreQP?) | boolean | Compare two states |
| isActiveRoute(name, params?, strict?, ignoreQP?) | boolean | Check if route is active |
| buildPath(name, params?) | string | Build URL path from route name |
Events & Plugins
| Method | Returns | Description |
| ----------------------- | ------------- | -------------------------------- |
| subscribe(listener) | Unsubscribe | Listen to successful transitions |
| usePlugin(...plugins) | Unsubscribe | Register plugin factories |
const unsub = router.subscribe(({ route, previousRoute }) => {
console.log(previousRoute?.name, "->", route.name);
});Standalone API
Tree-shakeable functions imported from @real-router/core/api. Only imported functions are bundled.
import {
getRoutesApi,
getDependenciesApi,
getLifecycleApi,
getPluginApi,
cloneRouter,
} from "@real-router/core/api";| Function | Purpose | Key methods |
| ---------------------------- | --------------------- | ---------------------------------------------------------------------------- |
| getRoutesApi(router) | Dynamic route CRUD | add, remove, update, replace, has, get |
| getDependenciesApi(router) | Dependency injection | get, set, setAll, remove, has |
| getLifecycleApi(router) | Guard registration | addActivateGuard, addDeactivateGuard, remove* |
| getPluginApi(router) | Plugin infrastructure | makeState, matchPath, addInterceptor, extendRouter, getRouteConfig |
| cloneRouter(router, deps?) | SSR cloning | Shares route definitions, independent state |
Utilities
SSR/SSG helpers imported from @real-router/core/utils.
import { getStaticPaths, serializeState } from "@real-router/core/utils";
// SSR: XSS-safe data embedding
const json = serializeState({ name: "home", path: "/" });
const html = `<script>window.__STATE__=${json}</script>`;
// SSG: enumerate all URLs for pre-rendering
const paths = await getStaticPaths(router, {
"users.profile": async () => [{ id: "1" }, { id: "2" }],
});
// → ["/", "/users", "/users/1", "/users/2"]| Function | Purpose |
| ---------------------------------- | ------------------------------------------------------------------------------------------- |
| serializeState(data) | XSS-safe JSON serialization for embedding in HTML <script> tags |
| getStaticPaths(router, entries?) | Enumerate leaf routes and build URLs for SSG pre-rendering |
| StaticPathEntries (type) | Type for the entries parameter: Record<string, () => Promise<Record<string, string>[]>> |
getNavigator(router) (main entry)
Frozen read-only subset of router methods for view layers. Pre-bound, safe to destructure. Imported from @real-router/core, not /api.
import { getNavigator } from "@real-router/core";// Dynamic route management
const routes = getRoutesApi(router);
routes.add({ name: "settings", path: "/settings" });
routes.replace(newRoutes); // atomic HMR-safe replacement
// Dependency injection for guards and plugins
const deps = getDependenciesApi(router);
deps.set("authService", authService);
// Global lifecycle guards
const lifecycle = getLifecycleApi(router);
lifecycle.addActivateGuard("admin", (router, getDep) => (toState) => {
return getDep("authService").isAuthenticated();
});
// SSR — clone with request-scoped deps
const requestRouter = cloneRouter(router, { store: requestStore });
await requestRouter.start(req.url);Route Configuration
import type { Route } from "@real-router/core";
const routes: Route[] = [
{
name: "admin",
path: "/admin",
canActivate: (router, getDep) => (toState, fromState, signal) => {
return getDep("authService").isAdmin();
},
children: [
{
name: "dashboard",
path: "/dashboard",
defaultParams: { tab: "overview" },
},
],
},
{
name: "legacy",
path: "/old-path",
forwardTo: "home", // URL alias — guards on source are NOT executed
},
{
name: "product",
path: "/product/:id",
encodeParams: ({ id }) => ({ id: String(id) }),
decodeParams: ({ id }) => ({ id: Number(id) }),
},
];Error Handling
Navigation errors are instances of RouterError with typed error codes:
import { RouterError, errorCodes } from "@real-router/core";
try {
await router.navigate("admin");
} catch (err) {
if (err instanceof RouterError) {
// err.code: ROUTE_NOT_FOUND | CANNOT_ACTIVATE | CANNOT_DEACTIVATE
// | TRANSITION_CANCELLED | SAME_STATES | DISPOSED | ...
}
}See RouterError and Error Codes for the full reference.
Validation
Runtime argument validation is available via @real-router/validation-plugin:
import { validationPlugin } from "@real-router/validation-plugin";
router.usePlugin(validationPlugin()); // register before start()
await router.start("/");The plugin adds descriptive error messages for every public API call. Register it in development, skip in production.
Documentation
Full documentation: Wiki
- Core Concepts — overview and mental model
- Defining Routes — nesting, path syntax, guards
- Navigation Lifecycle — transitions, guards, hooks
- RouterOptions —
defaultRoute,trailingSlash,allowNotFound, and more - Plugin Architecture — interception, extension, events
- Migration from router5
Related Packages
| Package | Description |
| ------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------- |
| @real-router/react | React integration (RouterProvider, hooks, Link, RouteView) |
| @real-router/browser-plugin | Browser History API and URL synchronization |
| @real-router/hash-plugin | Hash-based routing |
| @real-router/rx | Observable API (state$, events$, TC39 Observable) |
| @real-router/logger-plugin | Development logging |
| @real-router/persistent-params-plugin | Parameter persistence |
| @real-router/route-utils | Route tree queries and segment testing |
Contributing
See contributing guidelines for development setup and PR process.
