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.

1 min read

Install

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

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

Home.tsx
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.

Post.tsx
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.

tsx
// 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
  • push compiles 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 useParams reads path and query params as one object.
  • A root Router with no Slot fills the viewport, and its children are its routes.
  • Every declare module block merges into one global RegisterRoute, so you can declare each route next to the screen that renders it.
Edit on GitHub