JORVEL
Quickstart

Getting started

JORVEL scaffolds a complete micro-frontend workspace in five commands. By the end of this guide you'll have a host, a remote, file-based routing, and a dev server with HMR running on the same origin.

Who is this for?

Built for product teams that ship more than one frontend codebase and want a typed, opinionated runtime under their Module Federation. Comfortable defaults; nothing you can't override.

Prerequisites

  • Node.js 20, 22, or 24 on Linux, macOS, or Windows — all three are supported and exercised in CI. The CLI uses node:test / native fetch / structuredClone, so 18.x may work but is not covered.
  • pnpm 9.15+ is the supported package manager. npm 10+ and yarn 4+ also work — see Troubleshooting if symlinks or workspace resolution misbehave.
  • A terminal that can run npx / pnpm dlx, plus git for the generated project's default .gitignore / GitHub Actions templates.
  • Optional but recommended: VS Code with the TypeScript and ESLint extensions. Generated projects ship a .vscode/settings.json that enables workspace-relative typing and ESLint-on-save.

What does "production-ready" mean here?

The scaffold ships TypeScript strict-mode, ESLint, Vitest, Playwright, GitHub Actions for CI + deploy, a CSP-aware SSR template, and Rspack Module Federation pre-configured for React-singleton sharing. No follow-up wiring required to push to staging.
bash
corepack enable
corepack prepare pnpm@9.15.5 --activate

# shorthand
pnpm create jorvel my-app
# or
pnpm dlx jorvel@latest init my-app

Run in a terminal and init is interactive — it prompts for a template (host + remote · SaaS · blank), a package manager (pnpm / npm / yarn / bun), and Tailwind. Pass them as flags to skip the prompts:

bash
npm create jorvel@latest my-app -- --template saas --pm pnpm --tailwind
# create-jorvel forwards every flag to `jorvel init`

Try it in the browser

No install — open a starter in a StackBlitz sandbox and edit live:

Walkthrough

Create a running app

jorvel init scaffolds a complete, runnable app — the workspace (config, CI, deploy workflow, tooling) plus a starter React host + remote, already wired together. Like create-next-app: init, install, run. Add --tailwind for Tailwind; --no-app for a bare workspace.

bash
pnpm dlx jorvel@latest init my-app
cd my-app
pnpm install
pnpm dev            # host on :3000, loads the dashboard remote
# → apps/shell (host) + apps/dashboard (remote), federated + running

Add more remotes — auto-wired

Each new remote is automatically configured into the host— federation config, host route table, and the host's REMOTES map all updated. Pick a framework + language at the prompt, or pass flags. No manual wiring.

bash
jorvel generate remote pricing            # interactive: framework? · js/ts? · Tailwind?
jorvel generate remote billing --framework vue --lang ts
# both are now mounted in the host at /pricing/* and /billing/* — just refresh

Run the dev server

bash
jorvel dev --proxy-remotes --hmr-remotes

--proxy-remotesserves every remote on the host's origin so CSP, cookies, and SRI behave like production. --hmr-remotes reloads the host when a remote recompiles. Before launching, devchecks each app's port is free and fails fast (DEV-003) with the next free port if one is taken — skip with --no-port-check.

Add a route

Drop a file in apps/dashboard/src/pages/. JORVEL uses Next.js-style file conventions: index.tsx, [id].tsx, (group)/.

apps/dashboard/src/pages/settings.tsx
tsx
export default function Settings() {
  return (
    <main>
      <h2>Settings</h2>
      <p>Configure your account.</p>
    </main>
  );
}

Re-scan with jorvel routes --watch. The host now matches /dashboard/settings.

Build for production

bash
jorvel build              # all apps
jorvel build --app shell  # one app

Output lands under apps/<name>/dist/. Asset filenames carry content hashes; remoteEntry.js embeds SRI when federation.sri is on.

Deploy

bash
jorvel deploy --target vercel
vercel deploy

That's the whole flow.

Most teams need nothing else for their first deployment. The pages below cover advanced topics: typed routes, security headers, observability, and adapter customization.

Project layout

Every workspace follows the same conventions. Knowing where things live makes the rest of the docs read in any order.

text
my-app/
├── apps/
│   ├── shell/                  # Host — owns "/", layout, auth chrome
│   │   ├── public/             # Static assets served by Rspack dev-server
│   │   ├── src/
│   │   │   ├── App.tsx         # Root React component (used by both CSR + SSR)
│   │   │   ├── bootstrap.tsx   # Client entry — calls getRouter()
│   │   │   ├── index.ts        # async import of bootstrap (federation boundary)
│   │   │   └── jorvel.routes.ts  # Auto-generated by 'jorvel routes'
│   │   ├── jorvel.app.json       # App manifest: name, type, port, exposes
│   │   ├── jorvel.federation.json# Generated by 'jorvel federation'
│   │   └── rspack.config.mjs   # Rspack + ModuleFederationPlugin
│   └── dashboard/              # Remote — owns "/dashboard/*"
│       └── src/
│           ├── remote.tsx      # Exposed entry point (./App)
│           └── pages/          # File-based routes scanned by 'jorvel routes'
├── libs/                       # Shared libraries (contracts, ui-kit, etc.)
├── jorvel.config.json            # Workspace config: federation, security, deploy
├── pnpm-workspace.yaml         # pnpm workspace declaration
├── tsconfig.base.json          # Strict TS settings shared by every app
└── .github/workflows/          # CI: typecheck / lint / test / build

Anatomy of a request

Understanding what runs where saves hours when debugging. The dev-server walkthrough below traces a single page navigation end-to-end.

DiagramOne navigation in dev — host on :3000, remote proxied from :3001.

1. Browser hits the host

GET /dashboard/settings arrives at the Rspack dev-server on port 3000. The host serves index.html with its bootstrap chunk.

2. Host bootstraps

bootstrap.tsx calls getRouter() (singleton, StrictMode-safe) and mounts <RemoteOutlet routes remotes />. usePathname() returns /dashboard/settings.

3. Outlet matches the route

RemoteOutlet walks HOST_ROUTES, finds /dashboard/*{ remote: 'dashboard', module: './App' }, and triggers REMOTES.dashboard() — a native dynamic import('dashboard/App').

4. Federation loads the remote

Rspack's ModuleFederationPlugin fetches /jorvel/remotes/dashboard/remoteEntry.js (proxied to http://localhost:3001 by --proxy-remotes), bridges the React share scope, and resolves ./App.

5. Remote renders its sub-route

<RemoteApp subpath="/settings" pages={pages} /> matches settings.tsx from the generated jorvel.routes.ts, lazy-imports the chunk, and renders the page.

Daily commands cheat-sheet

The 80% of CLI flags you'll touch every day.

bash
# Develop
jorvel dev --proxy-remotes --hmr-remotes          # most common
jorvel routes --watch                              # in a second terminal, per remote

# Verify before pushing
jorvel typecheck                                   # tsc --noEmit per package
jorvel lint                                        # ESLint workspace-wide
jorvel test                                        # Vitest, parallel
jorvel perf                                        # bundle-size budgets
jorvel diagnose                                    # env, ports, configs

# Ship
jorvel build                                       # all apps, host first
jorvel build --app dashboard --compress            # one app + gz/br
jorvel deploy --target vercel                      # writes vercel.json

If something breaks

  1. Run jorvel diagnose. It checks Node, pnpm, Rspack versions, ports in use, generated federation configs, and React duplication risks.
  2. Set JORVEL_DEBUG=1 in your shell to surface full stack traces from CLI errors.
  3. See Troubleshooting for the most-hit issues (Invalid hook call, remote 404, hydration mismatch).

What's next?


Made it through the quickstart? Star the repo on GitHub.