Build your first router
This tutorial builds a small React app with two pages — Home and Profile — that navigate without a full page reload. By the end you'll have a router, two route views, and a working Link between them.
Prerequisites
- Node.js and a package manager (pnpm, npm, or yarn)
- Basic familiarity with React and Effector (
fork, stores, events) - Packages installed per Getting started:
@effector/router,@effector/router-react, andhistory
What you'll build
A single-page app with a Home route (/) and a Profile route (/profile). Clicking the link on either page navigates to the other, and the URL updates via the browser's History API.
The full example below lives in one file for clarity. In a real app you'd typically split routes, views, and the render entry point into separate modules — see Getting started for that layout.
1. Define routes
Routes are plain Effector units — no JSX, no components yet:
import { createRoute } from '@effector/router';
const homeRoute = createRoute({ path: '/' });
const profileRoute = createRoute({ path: '/profile' });2. Create the router
The router coordinates which route is open and reacts to navigation:
import { createRouter } from '@effector/router';
const router = createRouter({ routes: [homeRoute, profileRoute] });3. Create a view for each route
createRouteView ties a component to a route — it only renders while that route is open:
import { createRouteView, Link } from '@effector/router-react';
const Home = createRouteView({
route: homeRoute,
view: () => (
<div>
<h1>Home</h1>
<Link to={profileRoute}>Go to profile</Link>
</div>
),
});
const Profile = createRouteView({
route: profileRoute,
view: () => (
<div>
<h1>Profile</h1>
<Link to={homeRoute}>Back home</Link>
</div>
),
});Link takes a route object directly as to — no path strings to keep in sync.
4. Render whichever route is currently open
createRoutesView renders the first matching view from the list:
import { createRoutesView } from '@effector/router-react';
const RoutesView = createRoutesView({ routes: [Home, Profile] });5. Provide the router to the React tree
RouterProvider makes the router available to Link, Outlet, and the use* hooks used anywhere below it:
import { RouterProvider } from '@effector/router-react';
function App() {
return (
<RouterProvider router={router}>
<RoutesView />
</RouterProvider>
);
}6. Initialize the router with browser history
A router has no location until you give it one. setHistory wires it to a history instance inside an Effector scope, so navigation state is predictable from the very first render:
import { allSettled, fork } from 'effector';
import { Provider } from 'effector-react';
import { createRoot } from 'react-dom/client';
import { createBrowserHistory } from 'history';
import { historyAdapter } from '@effector/router';
const scope = fork();
async function main() {
await allSettled(router.setHistory, {
scope,
params: historyAdapter(createBrowserHistory()),
});
createRoot(document.getElementById('root')!).render(
<Provider value={scope}>
<App />
</Provider>,
);
}
main();Expected result
Open the app: you land on Home at /. Click "Go to profile" — the URL changes to /profile, the Profile view renders, and the browser's Back button returns you to Home, all without a full page reload.
Next steps
- Adapters reference — how
historyAdapterandqueryAdapterdecide where navigation state lives (browser history vs. query string), and how partial navigation targets are resolved. - Navigation lifecycle — what actually happens, in what order, between a navigation request and a route opening.
- React reference — the full API surface used above, plus
Outlet,withLayout, and theuse*hooks for nested routing.