Skip to content

Latest commit

 

History

History
202 lines (144 loc) · 5.23 KB

File metadata and controls

202 lines (144 loc) · 5.23 KB

logo

mobx-route

NPM version build status npm download bundle size

🚀 Simple and lightweight typed MobX router 🚀 Uses path-to-regexp power for path matching


Quick Start

import { createRoute } from "mobx-route";

const userDetails = createRoute("/users/:id");

// Path params are required — TypeScript enforces it
await userDetails.open({ id: 1 });

userDetails.isOpened; // true
userDetails.params;   // { id: "1" } — fully typed

✨ Features

🔗 Nested Routes with .extend()

Build route trees naturally — no config arrays, no <Routes> wrappers:

const users = createRoute("/users");
const userDetails = users.extend("/:userId");
const userPhotos = userDetails.extend("/photos");

// Path is auto-concatenated: /users/:userId/photos
await userPhotos.open({ userId: 42 });
// → /users/42/photos

users.isOpened;        // true (parent is open too)
users.hasOpenedChildren; // true

🛡️ Route Guards & Redirects

Protect routes with beforeOpen — cancel navigation or redirect:

const dashboard = createRoute("/dashboard", {
  beforeOpen: async () => {
    if (!await isAuthenticated()) {
      return { url: "/login", replace: true }; // redirect
    }
    // return undefined → proceed
  },
  checkOpened: () => currentUser.isAuthorized, // reactive predicate
});

🔮 Virtual Routes for Modals & Drawers

Same .open() / .close() / .isOpened API — but no URL involved:

const authModal = createVirtualRoute({
  checkOpened: (route) => route.query.data.modal === "auth",
  open: (_, route) => route.query.update({ modal: "auth" }),
  close: (route) => route.query.update({ modal: undefined }),
  beforeClose: () => !hasUnsavedChanges, // prevent closing
});

authModal.isOpened;  // reactive — auto-updates from query
authModal.isClosing; // for exit animations

🎯 Typed Query Params

const search = createRoute<
  "/search",
  {},
  {},
  { q: string; page?: number; sort?: "asc" | "desc" }
>("/search");

// TQueryParams types the INPUT — what you pass to open()
await search.open({}, { query: { q: "mobx", page: 1 } });

// query.data is always Record<string, string> at runtime (values come from URL)
search.query.data.q;    // string
search.query.data.page; // string | undefined — use Number() or QueryParam for typed access

🔄 update() for In-Place Changes

Replace params without polluting browser history:

await userRoute.open({ userId: 1 }, { query: { tab: "profile" } });
await userRoute.update({ userId: 2 });
// → /users/2?tab=profile (replace: true, mergeQuery: true by default)

🧩 React Integration

import { RouteView, RouteViewGroup, Link } from "mobx-route/react";

// Declarative route rendering
<RouteView route={userRoute} view={UserPage} fallback={<Loading />} />

// Route switching with fallback
<RouteViewGroup otherwise={notFoundRoute}>
  <RouteView route={homeRoute} view={HomePage} />
  <RouteView route={userRoute} view={UserPage} />
  <div>Not found</div>
</RouteViewGroup>

// Type-safe links
<Link to={userRoute} params={{ userId: 42 }}>Profile</Link>

🧠 View Model Integration

import { RouteViewModel } from "mobx-route/view-model";

class UserPageVM extends RouteViewModel<typeof userRoute> {
  route = userRoute;
  // payload, pathParams, query, isMounted — all built-in
}

🌍 Optional Path Segments & Wildcards

// Optional segment
const route = createRoute("/users{/:tab}");
route.open();          // → /users
route.open({ tab: 1 }); // → /users/1

// Wildcard/rest params
const docs = createRoute("/docs/*rest");
docs.open({ rest: ["api", "v2", "auth"] }); // → /docs/api/v2/auth

📦 Tree-Shakeable Subpath Exports

Only pay for what you use:

import { createRoute } from "mobx-route";              // core only
import { RouteView, Link } from "mobx-route/react";    // + React
import { RouteViewModel } from "mobx-route/view-model"; // + VM

Installation

npm install mobx-route
# or
pnpm add mobx-route
# or
yarn add mobx-route

Peer dependencies (React integration is optional):

npm install mobx
# For React:
npm install mobx-react-lite react react-dom

Contribution Guide

Want to contribute? Follow this guide


License

MIT