Router & Route
How routes match, how to register them, and how to set defaults
<Router> is the root container. It picks which <Route> to render based on the URL.
The pieces
<Router>
<Route path="/" element={<Home />} />
<Route path="/posts/:slug" element={<Post />} />
</Router>| Element | Job |
|---|---|
<Router> | Sets up history, transitions, and decorators |
<Route> | Maps a path (or paths) to an element |
Path patterns
flemo uses path-to-regexp v8 for matching.
"/"; // exact
"/posts/:slug"; // a single param
"/users/:id/posts/:p"; // multiple params
"/files/*splat"; // wildcard (note the `*` prefix on the param)You can also pass an array to share one component across paths:
<Route path={["/", "/home"]} element={<Home />} />Type-safe routes
Augment RegisterRoute and navigate.push, useParams, etc. all type-check against it. You can
declare every route in one central file, or split the declaration across page files. TypeScript
merges all the augmentations, so either approach works. Most projects keep it at the bottom of
the file where <Router> is mounted.
declare module "@flemo/react" {
interface RegisterRoute {
"/": undefined;
"/posts/:slug": { slug: string };
"/users/:id": { id: string };
}
}Routes without params map to undefined. Routes with params use the inferred shape.
navigate.push("/posts/:slug", { slug: "hello" }); // ✅
navigate.push("/posts/:slug", { id: "1" }); // ❌ TS error
navigate.push("/unknown"); // ❌ TS errorRouter options
<Router initPath="/" defaultTransitionName="cupertino" transitions={[]} decorators={[]}>
{/* routes */}
</Router>| Prop | Default | What it does |
|---|---|---|
initPath | "/" | Path used during SSR before window.location is available |
defaultTransitionName | "cupertino" | Transition used when a navigate.push doesn't specify one |
transitions | [] | Custom transitions to register (see Transitions) |
decorators | [] | Custom decorators (e.g., overlays) to register |
Server-side rendering
flemo is a client-side SPA router. It drives window.history and takes over navigation once
it mounts. <Router> can still render on the server for the first paint, but the server has no
window.location, so you tell it which route to render first with initPath.
- Server: renders the screen matching
initPath. Defaults to"/". - Client: on mount, reads
window.location.pathnameand takes over from there.
Pass the request path through initPath and the first paint matches the URL, with no hydration
mismatch. This works with any setup that renders React to a string and lets flemo own routing
from there:
// On the server, hand the incoming request path to initPath.
<Router initPath={requestPath}>{/* routes */}</Router>Pure-SPA setups (Vite, etc.) don't need initPath at all. The client reads
window.location.pathname on its own.
Because flemo owns client-side history (it pushes to window.history and listens for popstate),
it does not compose with a host framework that also owns routing, notably the Next.js App
Router. The two routers fight over navigation and the URL, so flemo can't act as the router for
a Next.js app's pages. Use flemo in a pure SPA, or mount it as a self-contained, client-only
island that doesn't share routing with the host framework.