Getting started
Getting started
Build the smallest complete flemo app: two screens, one typed route param, and a push you can pop with Back, a button, or a swipe.
Install
pnpm add @flemo/react@flemo/react needs react and react-dom 19.2.8 or later as peer dependencies.
Svelte and SolidJS support is planned.
Mount the Router
Router manages the screen stack. Each Route sets the screen that renders for one path.
import { Route, Router } from "@flemo/react";import Home from "./Home";import Post from "./Post";export default function App() { return ( <Router> <Route path="/" element={<Home />} /> <Route path="/posts/:slug" element={<Post />} /> </Router> );}Build a screen and register its route
Every route component renders a Screen. To open another screen, call navigate.push with the route pattern and its params.
Augment RegisterRoute so TypeScript checks every path and params object. Until you do, push accepts no path at all.
import { Screen, useNavigate } from "@flemo/react";export default function Home() { const navigate = useNavigate(); return ( <Screen> <h1>Home</h1> <button onClick={() => navigate.push("/posts/:slug", { slug: "hello" })}> Open hello </button> </Screen> );}// A route without params maps to undefined.declare module "@flemo/react" { interface RegisterRoute { "/": undefined; }}Read params and pop
useParams returns the screen's params, typed by its registered route. navigate.pop() goes back one screen.
import { Screen, useNavigate, useParams } from "@flemo/react";export default function Post() { const navigate = useNavigate(); const { slug } = useParams<"/posts/:slug">(); return ( <Screen> <h1>{slug}</h1> <button onClick={() => navigate.pop()}>Back</button> </Screen> );}// A dynamic route maps to its param shape.declare module "@flemo/react" { interface RegisterRoute { "/posts/:slug": { slug: string }; }}Tap Open hello to push the post. Go back with browser Back, the Back button, or a drag from the left edge.
Pick a transition
Every navigation plays cupertino by default. Change the default on the Router, or pass transitionName to one call.
// Every navigation in this Router uses material.<Router defaultTransitionName="material"> <Route path="/" element={<Home />} /> <Route path="/posts/:slug" element={<Post />} /></Router>;// This one push uses layout.navigate.push("/posts/:slug", { slug: "hello" }, { transitionName: "layout" });The built-in presets are cupertino, material, layout, and none. See for gestures and custom motion.
What just happenedDetails
pushcompiles the pattern and params into a URL, adds a real browser history entry, and plays the transition.- Params the path does not use become the query string, and
useParamsreads path and query params as one object. - A root
Routerwith noSlotfills the viewport, and its children are its routes. - Every
declare moduleblock merges into one globalRegisterRoute, so you can declare each route next to the screen that renders it.