Core concepts
JORVEL is opinionated. Before you wire it up it helps to understand what each layer is, what guarantees it makes, and where the seams are. This page is the 10-minute orientation.
Host vs. remote
Every JORVEL workspace contains exactly one host (the shell that owns the URL bar, layout, and global state) and one or more remotes(independently deployable apps that mount sub-trees of the URL). They're both real React apps; the only difference is whose URL the user sees.
my-app/
├── apps/
│ ├── shell/ # host — owns "/", "/login", layout chrome
│ │ └── jorvel.app.json # { "type": "host", "port": 3000 }
│ ├── dashboard/ # remote — owns "/dashboard/*"
│ └── billing/ # remote — owns "/billing/*"At runtime, the host fetches each remote's remoteEntry.js only when its URL is hit. Remotes can be deployed to a totally different CDN, on a different release cadence, by a different team.
The federation contract
Module Federation is a runtime contract: a remote exposes module IDs, the host consumes them by name. JORVEL gives you a typed wrapper.
import { defineFederationContract } from '@jorvel/types';
export const dashboardContract = defineFederationContract({
name: 'dashboard',
exposes: {
'./App': './src/remote.tsx',
'./widgets/UsageChart': './src/widgets/UsageChart.tsx',
},
emits: {
'dashboard:opened': null,
'dashboard:action': { action: 'string', payload: 'unknown?' },
},
listens: {
'shell:ready': { timestamp: 'number' },
},
});Now the host can useEventBus<DashboardContract>() with full IntelliSense.
The runtime contract
Both host and remote import @jorvel/runtime, and the package is configured as a Module Federation singleton. That means navigation, prefetch caches, and the event bus are a single object across the whole page — no duplicate state.
Singleton or you're going to have a bad time
@jorvel/runtime, @jorvel/event-bus, or @jorvel/statearen't singletons, the host and remote will see different instances and silently lose events. The CLI sets this up by default — only override it if you know exactly why.Rendering modes
- SPA — the host bootstraps in the browser, lazy-loads remotes on navigation. Default for dev.
- SSR —
@jorvel/ssrrenders matched routes on the server, streams to the browser, and hydrates. Edge-runtime safe. - SSG —
staticExport()pre-renders a fixed list of routes to disk with a content-hash manifest. Worker-pool parallelism by default.
Security primitives
Federation surfaces three threat classes: untrusted remotes, XSS through hydration payloads, and CDN tampering. @jorvel/security ships:
- Origin allowlist with
*single-label and**multi-label wildcards. - SRI for
remoteEntry.js— Web Crypto, edge-runtime safe. - CSP builder with
strict-dynamic+ base64url-validated nonce. - safeJsonForScript for hydration state injection.
Release model
JORVEL uses Changesets. The configured linked groups are [runtime, ssr, security], [state, event-bus, events], and [adapter-*]. CLI / types / UI / observability / rspack-route-assets bump independently. Examples and docs are ignored.
Lifecycle at a glance
Every interactive frame goes through the same five stages. Drop a breakpoint at any of them when debugging.
Design principles
Two opinions shape every API. Knowing them up front explains the smaller decisions.
- Globals beat prop-drilling for cross-app state.
@jorvel/runtime,@jorvel/event-bus, and@jorvel/statepin singletons toglobalThis— so two bundles of the same package still observe one router, one bus, one store registry. The federation share-scope is the fast path;globalThisis the safety net. - Boundaries are HTTP requests, not React props. A remote crashing should not crash the host.
RemoteOutletwraps each load in anErrorBoundary,fetchHealth()can gate a load, andRemoteRegistryrejects URLs outside the configured allowlist.
When NOT to use JORVEL
- One frontend team. Module Federation buys independent deploys; without ownership splits the overhead is pure cost. Pick Next.js / Astro / Vite-React instead.
- Static marketing sites.SSG-only? Use Astro. JORVEL's SSG is a feature of a federated app, not a destination.
- Server-driven React (RSC).Not supported — federation + the RSC wire format aren't compatible upstream yet. JORVEL ships islands, streaming SSR,
use(promise), and hydratable server stores instead. Need RSC today? Ship Next.js App Router.
What's next?
