Getting Started
JARL is split across two packages: jarl-atoms, framework-agnostic jotai
atoms that do the actual route matching, and jarl-react, React bindings (components + hooks)
over them. To get started you need three things: some route atoms, a jotai <Provider> at the
root of your app, and Link/Route from jarl-react to navigate and render.
Route Atoms
routes.ts:
import { rootAtom, staticRouteAtom } from "jarl-atoms";
export const homeRoute = rootAtom;
export const aboutRoute = staticRouteAtom("about");
Each of these is a route atom: a jotai atom that, when read, tells you whether its path
currently matches (match, exact, values) and how to build a URL for it (reverse); when
written, it navigates there. rootAtom matches / itself and is the implicit parent every
other route atom builds on unless you give it a different parent. staticRouteAtom("about")
matches a single fixed path segment - here, /about.
Unlike a v1-style route table, there's no single object describing your whole site: each page is its own atom, composed out of smaller ones (see the Path Variables guide for nesting and dynamic segments).
The next task is to make these atoms live. Route atoms read/write a shared locationAtom
that's ultimately backed by the browser's history API (via jotai's own Provider/store, no
separate history package to configure), so all you need at the root of your app is a
<Provider>.
main.tsx:
import { createRoot } from "react-dom/client";
import { Provider } from "jotai";
import App from "./App";
createRoot(document.getElementById("root")!).render(
<Provider>
<App />
</Provider>,
);
Finally, render based on which route atom currently matches, using <Route> from jarl-react:
App.tsx:
import { Route } from "jarl-react";
import { homeRoute, aboutRoute } from "./routes";
import { HomePage, AboutPage } from "./pages";
export default () => (
<>
<Route on={homeRoute} exact>
<HomePage />
</Route>
<Route on={aboutRoute} exact>
<AboutPage />
</Route>
</>
);
exact means "only render when this is the final matched segment, not just because a
descendant route also matches" - without it, homeRoute (which everything else is built on
top of) would match on every page, not just /.
There is one piece missing of course - we can't actually navigate between the pages yet! Let's
see what a Menu component will look like, using Link:
Menu.tsx:
import { Link } from "jarl-react";
import { homeRoute, aboutRoute } from "./routes";
export default () => (
<nav>
<Link route={homeRoute} to={{}} exact>
Home
</Link>
<Link route={aboutRoute} to={{}}>
About
</Link>
</nav>
);
Link takes the route atom to link to plus the param values for it ({} here, since neither
route has any dynamic segments) and reverses them back into an href - so your route atoms
stay the single source of truth for URL shape in both directions, exactly like a v1 location
object did, just expressed as atoms instead of a route table.
That's it for the basics! Continue to Path Variables for dynamic segments, or Data Loading for fetching data as part of a route match.