@octanejs/remix-router
v0.1.65
Published
react-router for the octane renderer — the framework-agnostic router core vendored byte-close from react-router v8, with the React layer (RouterProvider, hooks, Link, boundaries) transcribed onto octane.
Keywords
Readme
@octanejs/remix-router
react-router for the octane UI framework.
Installation
npm install @octanejs/remix-router
pnpm add @octanejs/remix-routerreact-router v8 ships as a single package, so this port vendors the framework-agnostic router core byte-close (loaders, actions, redirects, matching, history — validated by 161 ported upstream router tests plus four focused v8.2 regression pins) and transcribes the React layer onto octane's hooks. The shipped runtime surface matches react-router 1:1 — existing code works by changing the import.
The port is complete — full export parity. Data mode, declarative mode
(MemoryRouter, <Routes>/<Route>, <Navigate>), the DOM entry points
(BrowserRouter/HashRouter, createBrowserRouter/createHashRouter,
NavLink, useSearchParams), mutations (Form, useSubmit,
useFetcher/useFetchers), guards + scroll (useBlocker,
unstable_usePrompt, ScrollRestoration, useBeforeUnload,
useViewTransitionState), static SSR (StaticRouter,
StaticRouterProvider, createStaticHandler/createStaticRouter on
octane/server), and the cookie/session server runtime (createCookie,
createSession, createCookieSessionStorage, …). Framework mode
(Meta/Links/Scripts, createRequestHandler) and RSC are permanently out
of scope — those names exist as throwing stubs so
tests/conformance/parity.test.ts pins the surface at exact parity
(docs/remix-router-port-plan.md has
the scope policy).
// before
import { createBrowserRouter, RouterProvider, Link, useLoaderData } from 'react-router';
// after
import { createBrowserRouter, RouterProvider, Link, useLoaderData } from '@octanejs/remix-router';
const router = createBrowserRouter([
{
path: '/',
Component: Layout,
children: [
{ index: true, Component: Home },
{ path: 'users/:id', loader: fetchUser, Component: User, errorElement: <Oops /> },
],
},
]);
function App() @{
<RouterProvider router={router} />
}
function User() @{
const user = useLoaderData();
<h1>{user.name as string}</h1>
}Entry points
| import | what you get | notes |
| --- | --- | --- |
| @octanejs/remix-router | the vendored core surface (matchPath, redirect, data, …) + createMemoryRouter, RouterProvider, Outlet, Await, Link, useLinkClickHandler, the full read-hook family (useLoaderData, useNavigate, useNavigation, useRouteError, …), declarative mode (MemoryRouter, Routes/Route, Navigate, createRoutesFromChildren/Elements), the DOM layer (createBrowserRouter/createHashRouter, BrowserRouter/HashRouter/unstable_HistoryRouter, NavLink, useSearchParams), mutations (Form, useSubmit, useFormAction, useFetcher/useFetchers), guards/scroll (useBlocker, ScrollRestoration, …), static SSR (StaticRouter/StaticRouterProvider/createStaticHandler/createStaticRouter), and cookies/sessions (createCookie, createCookieSessionStorage, …) | full parity |
| @octanejs/remix-router/dom | RouterProvider with octane's flushSync wired in | mirror of react-router/dom |
React Router 8 migration
- React Router 8 no longer publishes a
react-router-dompackage. Import everything from@octanejs/remix-router, except the flushSync-enabledRouterProvider, which comes from@octanejs/remix-router/dom. - Middleware is always enabled. Loader, action, and middleware
contextis aRouterContextProvider; thefuture.v8_middleware,future.v8_passThroughRequests, andfuture.v8_trailingSlashAwareDataRequestsflags are gone. - The internal
hasErrorBoundaryroute property is gone. UseErrorBoundaryorerrorElement. - The package follows Octane's repository-wide Node.js 22.22.2 or newer runtime contract.
How it works
- The router core under
src/lib/router/is vendored verbatim from react-router 8.2.0 (scripts/vendor-remix-router.mjs; hand-edits prohibited). The script applies three documented deviations: two Octane integration points, and edits to unused declarations so the source typechecks under an application'snoUnusedLocalsandnoUnusedParameters. RouterProviderpreserves upstream's exact commit paths —startTransitionwrapping, theflushSyncoption, and the ViewTransition dance (dormant until Phase E) — on octane'suseState/useLayoutEffect/useOptimistic/startTransition/flushSync.errorElement/useRouteErrorrun on octane's@try/@catch(octane has no class components);<Await>suspends through octane'suse()with upstream's TrackedPromise decoration kept verbatim, souseAsyncValue/useAsyncErrorare unchanged. Render-prop children work via octane'sisChildrenBlockguard.<Routes>accepts BOTH children forms: descriptor children (createRoutesFromElements,{expr}arrays) are walked upstream-style, while the natural.tsrxblock-children authoring goes through a registration collector — each<Route>registers its config in a pre-paint layout effect, so the first paint already shows the matched route. Registration order is mount order, which differs from upstream's source order only formatchRoutesscore ties (pinned + documented).- Refs are props (no
forwardRef); hooks are keyed by octane's compiler-injected per-call-site slots, forwarded through every custom hook.
Status
Current scope, known divergences, and verification status are tracked in the
generated bindings status table, sourced from
this package's status.json.
