flemo

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>
ElementJob
<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.

App.tsx
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 error

Router options

<Router initPath="/" defaultTransitionName="cupertino" transitions={[]} decorators={[]}>
  {/* routes */}
</Router>
PropDefaultWhat 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.pathname and 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.

On this page