JORVEL

Migration guides

Two starting points: a classic SPA (Create React App + react-router) you want to split into federated remotes, or an existing hand-rolled Module Federation setup you want JORVEL's conventions on top of. Both are incremental — JORVEL doesn't require a big-bang rewrite.

From Create React App + react-router

CRA is unmaintained and has no federation story. The migration is two phases: get the app building under JORVEL's Rspack pipeline, then carve out remotes.

1. Scaffold a workspace and move the app in

bash
npx jorvel init my-workspace        # host + one remote skeleton
cd my-workspace
# copy your CRA src/ into apps/shell/src, then:
pnpm install

2. Swap react-router for the JORVEL router

The two-tier router is History-API based — no <BrowserRouter> needed. Map routes to the host table and use NestedRouter for in-app layouts.

tsx
// before (react-router v6)
<Routes>
  <Route path="/dashboard/*" element={<Dashboard />} />
</Routes>

// after (JORVEL) — see /docs/routing and /docs/nested-routes
import { NestedRouter, type NestedRoute } from '@jorvel/runtime';
const routes: NestedRoute[] = [
  { path: '/dashboard', element: <DashboardShell />, children: [
    { index: true, lazy: () => import('./pages/overview.js'), loading: <Skeleton /> },
  ] },
];
react-routerJORVEL
useNavigate()useNavigate() (@jorvel/runtime)
useParams()useOutletParams() / useParams()
useSearchParams()useSearchParams()
<Outlet /><Outlet />
<Link to><NavLink to label>
loader / actiondefineLoader (ssr) / defineAction

3. Carve out the first remote

Pick a route subtree owned by another team. Move it to apps/<remote>, expose ./App, and wire it into the host:

bash
jorvel add remote billing --port 3002
jorvel federation         # regenerate jorvel.federation.json
jorvel federation diff --base main   # confirm the host contract is intact
jorvel dev

From an existing Module Federation setup

If you already run Webpack/Rspack ModuleFederationPlugin by hand, JORVEL adds conventions, a typed contract layer, and tooling without forcing you to relinquish control.

  1. Translate each app's plugin config into a jorvel.app.json (name, type: host | remote, port) and run jorvel federation — it emits the jorvel.federation.json JORVEL consumes. Diff it against your current plugin options to confirm parity.
  2. Replace your bespoke remote loader with loadRemoteEntry / loadRemoteModule from @jorvel/runtime — you get the origin allowlist, SRI enforcement, version-skew warnings, and last-good fallback for free.
  3. Adopt defineFederationContract for the modules a host depends on, then gate PRs with jorvel federation diffso a remote can't silently drop an expose.

Codemods

Coming from the pre-rename @mfjs/* packages? jorvel migrate ships three codemods (mfjs-to-jorvel, builtins-define, routes-host-rename). Dry-run by default; --apply commits.

Migrate incrementally

Keep the host shippable at every step. Move one route subtree to a remote, verify with jorvel federation diff + contract tests, deploy, then repeat. A big-bang split loses you the ability to bisect a regression.