> For AI agents: the complete documentation index is available at /llms.txt, the full documentation bundle is available at /llms-full.txt.

# UltraModern.js 3.0 vs Modern.js 3.0

This page compares **UltraModern.js 3.0** with the Modern.js 3.x line this fork tracks. The baseline is the current merged Modern.js `release-v3.x` baseline, not a frozen patch-version snapshot.

UltraModern.js 3.0 is our SuperApp framework forked from Modern.js. It keeps the Modern.js plugin/runtime mental model where that helps adoption, but it is positioned as a separate framework for Effect HttpApi-first HTTP APIs, TanStack Router, SSR, Module Federation, and independently deployable Micro Verticals.

## Design Principles

- Keep deltas explicit and auditable.
- Keep upstream Modern.js merge compatibility explicit: generic Modern.js runtime
  code can remain where it belongs, while UltraModern-generated surfaces stay
  strict.
- Add platform-level contracts only where they improve cross-team reliability.
- Keep escape hatches explicit and outside the generated HTTP API path.

## Current Workspace Contract

UltraModern.js uses Effect HttpApi, TanStack Router, SSR, and independently deployable Micro Verticals. Generated workspaces use `api/index.ts`, `shared/api.ts`, and `src/api/*` with concrete Effect schemas. The CLI creates, adds to, and validates workspaces against the current contract.

## Intentional Differences (v3 line)

| Area                            | Modern.js 3.0 baseline                                       | UltraModern.js 3.0                                                                                                                                                                                                                 |
| ------------------------------- | ------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Build diagnostics               | RsDoctor is generally opt-in                                 | Adds a first-class `performance.rsdoctor` config surface (opt-in; the earlier default-on behavior and diagnostics contract artifact were reverted)                                                                                 |
| Output and static serving       | Precompression behavior is app-defined                       | Enables `output.precompress` by default and serves `.br` / `.gz` variants via `Accept-Encoding` negotiation                                                                                                                        |
| BFF runtime and contracts       | Standard BFF runtime/client generation                       | Adds `requestId`-aware producer isolation, fail-fast initialization checks, and operation/trace correlation headers                                                                                                                |
| BFF runtime choices             | Standard request-handler oriented BFF paths                  | Uses Effect `HttpApi` as the only generated HTTP API path (`api/index.ts`, `shared/api.ts`, `src/api/*`) and rejects raw handlers, manual parsing, manual `Response` construction, and Hono server imports in generated workspaces |
| Telemetry standardization       | Observability wiring is often app-specific                   | Adds framework-level telemetry pipeline with OTLP/VictoriaMetrics exporters, redaction, batching, and backpressure controls                                                                                                        |
| App-level MF SSR handshake      | No dedicated super-app app-level stability contract focus    | Adds `server.ssr.moduleFederationAppSSR` plus integration-tested env/config handshake                                                                                                                                              |
| MF vertical loading reliability | Retry/fallback patterns are often implemented per app        | Adds deterministic timeout/network/contract-error reliability matrix and distributed OTEL continuity tests                                                                                                                         |
| Module onboarding governance    | No module-certification evidence profile in baseline         | Adds module SDK contracts, boundary anti-pattern guards, and release/module certification gate workflows                                                                                                                           |
| Router runtime                  | Default runtime path centers on React Router                 | Adds first-class TanStack Router runtime/CLI path (React Router remains supported)                                                                                                                                                 |
| Scaffolding templates           | Default create templates center on React Router starter path | Generates a SuperApp workspace by default with TanStack, Effect, Module Federation, i18n, Tailwind, Cloudflare, and ownership contracts                                                                                            |
| Workspace preset enforcement    | No generated UltraModern preset gate workflow                | Generated workspaces include `.github/workflows/ultramodern-workspace-gates.yml`, `pnpm check`, primitive local gates, and generated contract validation                                                                           |

## What We Intentionally Do Not Change

- We do not hide the fork behind legacy Modern.js branding.
- We do not optimize for generic Modern.js defaults when they conflict with SuperApp reliability.
- We do not delete generic Modern.js Hono/file-convention runtime support just
  to enforce UltraModern policy; the enforcement lives in UltraModern
  generator, config, checks, and generated workspaces.
- Generated UltraModern API work uses the Effect runtime only; raw Hono/function handlers are not part of the generated API architecture.
- We make incompatible scaffold changes when they remove architecture drift.

## Create and Validate a Workspace

```bash
pnpm dlx @bleedingdev/modern-js-ultramodern-create@latest catalog --vertical
mise install
mise exec -- pnpm install
mise exec -- pnpm check
mise exec -- pnpm build
```

Localized Cloudflare SSR workspaces redirect `/` on the server to the negotiated locale. For example, `Accept-Language: cs-CZ` returns `302` with `Location: /cs`, `Cache-Control: private, no-store`, and `Vary` covering locale detection headers. Validate the deployed Worker with:

```bash
curl -I -H 'Accept-Language: cs-CZ,cs;q=0.9,en;q=0.1' https://<worker-host>/
```

Generated strict Effect workspaces pin the compatible Effect cohort with pnpm
overrides: `effect@4.0.0-rc.112`, `@effect/opentelemetry@4.0.0-rc.112`,
and `@effect/vitest@4.0.0-rc.112`. Do not add app-local direct Effect
versions that disagree with those overrides. The strict 24-hour release-age
gate applies to installed packages; this cohort carries no Effect age
exemption, and override-only `@effect/vitest` is not an installed approval
target. Independently, exact `trustPolicyExclude` entries for `effect`
and `@effect/opentelemetry` cover their trusted-publisher to provenance
metadata transition; they are not release-age approvals.

### Current generated dependency baseline

| Surface                        | Pin                                                                                                 |
| ------------------------------ | --------------------------------------------------------------------------------------------------- |
| Effect runtime and test cohort | `effect`, `@effect/opentelemetry`, and `@effect/vitest`: `4.0.0-rc.112`                             |
| Effect compiler                | `@effect/tsgo@0.41.0`                                                                               |
| Oxc and Ultracite              | `oxlint@1.81.0`, `oxfmt@0.66.0`, `ultracite@7.11.0`                                                 |
| TanStack Router                | `@tanstack/react-router@1.170.33`, `@tanstack/router-core@1.171.28`, `@tanstack/history@1.162.2`    |
| Module Federation              | `bridge-react`, `manifest`, `modern-js-v3`, and `rspack`: `2.9.0`; `@module-federation/node@2.7.50` |
| Tailwind CSS                   | `tailwindcss@4.3.3`                                                                                 |
| Node and package tooling       | Node `26.7.0`, `@types/node@^26.4.1`, pnpm `11.24.0`                                                |

Generated workspaces default to Module Federation bridge-react's router-free
base entry (`bridge.enableBridgeRouter: false`), with TanStack Router as the
application router. Declaring `react-router` (or `react-router-dom`) as a
direct dependency of an app marks it a React Router consumer: the
generator/migration then emit `enableBridgeRouter: true` for that app's
federation config, the workspace validator accepts it, and the UltraModern
preset aliases `react-router`'s entrypoints for the app's own routing.

Gradual migration means old, unmigrated Modern.js apps can keep their existing
runtime until they are converted. Once a surface is generated or migrated as
UltraModern HTTP API, it uses Effect HttpApi only; do not keep Hono, lambda
handlers, or raw request handlers inside that UltraModern API surface.

When migrating a generated workspace that already has nested Effect API files, make the
move explicit and do not keep compatibility aliases:

1. Move each vertical server entry from `verticals/<id>/api/effect/index.ts` to
   `verticals/<id>/api/index.ts`.
2. Move each shared API contract from `verticals/<id>/shared/effect/api.ts` to
   `verticals/<id>/shared/api.ts`.
3. Move generated clients from `verticals/<id>/src/effect/*-client.ts` to
   `verticals/<id>/src/api/*-client.ts`.
4. Move shell API aggregates from `apps/shell-super-app/src/effect/*` to
   `apps/shell-super-app/src/api/*`.
5. Update imports and package exports to use `./api`, `./api/client`, and
   `@<workspace>/<vertical>/api/client`; remove `./effect/client`,
   `./shared/effect/api`, and shared Effect API packages.
6. Update every vertical `modern.config.ts` to use
   `bff.effect.entry: './api/index'` and
   `bff.effect.strictEffectApproach: true`.
7. Update topology so API metadata lives directly under `api` with
   `api.bff.strictEffectApproach: true`; do not keep `api.effect`.
8. Run `pnpm api:check`,
   `scripts/validate-ultramodern-workspace.mts`, `pnpm check`, and
   `pnpm build`.

If a migration fails, fix the owning generated contract, topology, package
exports, or API module. Do not add app-level aliases, raw request handlers,
manual `Response` construction, local type casts, or package shims to make the
old layout pass.

Effect RPC, WebSockets, and other transports should be added as explicit
transport surfaces when needed. They do not make raw request handlers valid
inside generated HTTP API modules.

Strict API tests should exercise the `HttpApi` contract. Use
`createEffectBffTestHandler` from `@modern-js/bff-effect/effect-edge` for
edge-compatible proof tests; if you manually compose a web handler, provide
`HttpServer.layerServices` beside your API group layer before calling
`HttpRouter.toWebHandler`.

For dynamic origin CORS predicates inside strict Effect HTTP APIs, prefer
`HttpRouter.middleware(Effect.succeed(HttpMiddleware.cors(...)))`.
`HttpRouter.middleware(...)` returns a `Layer` directly in the pinned Effect
cohort; do not read a `.layer` property from it.

Use one package-source strategy per repo. The published BleedingDev create
package defaults to `--ultramodern-package-source=install` and records the
exact cohort in `.modernjs/ultramodern.json`. Keep `--workspace` only for local
monorepo testing against unreleased packages. Release proof and CI should pin
the exact cohort with `--ultramodern-package-version` when a repo must prove a
specific published framework version.

Older repos with nested Effect entries and `verbatimModuleSyntax` should update to
the latest BleedingDev cohort instead of adding app-level `"type": "module"`
metadata, Module Federation shims, or custom server wrappers. The framework BFF
compiler normalizes CommonJS server output while generated app packages keep
stable TypeScript 7 so Module Federation DTS generation and TS-Go checks use
the same modern compiler lane.

Cloudflare SSR deploys are also split by runtime responsibility: `.output` is an
ESM module-worker package, while `.output/worker` remains a CommonJS package
scope for the worker bundles emitted by Modern.js SSR builds. Do not fix older
repos by hand-editing generated worker output; upgrade the framework cohort and
rerun the generated validation instead.

After installing the new cohort, run the generated
`scripts/validate-ultramodern-workspace.mts` contract check before accepting
manual edits. Fix topology, ownership, package-source, local overlay, generated
contract, Tailwind prefix, or Module Federation conflicts in the owning files
instead of patching generated output by hand.

Cloudflare D1 bindings are first-class on `deploy.worker.d1Databases`; use that
config instead of app-local postprocessing when a generated app owns D1
migrations. Cloudflare public output excludes server-only `api` and `shared`
directories by default, and generated Modern/Rspack output roots, temp
directories, and caches are isolated per app, build target, and Rspack
environment so local `build`, `cloudflare:build`, and multi-environment
compilers do not share mutable build state.

## Configure the public preset

Use `presetUltramodern(appConfig, presetOptions)` inside the normal Modern.js
`defineConfig` call:

```ts title="modern.config.ts"
import { defineConfig, presetUltramodern } from '@modern-js/app-tools';

export default defineConfig(
  presetUltramodern(
    {
      output: {
        precompress: false,
      },
      server: {
        telemetry: {
          failLoudStartup: true,
        },
      },
    },
    {
      appId: 'catalog',
      enableModuleFederationSSR: false,
      enableTelemetryExporters: false,
    },
  ),
);
```

The first argument is ordinary `AppUserConfig`. It contains app-owned config
and wins where it supplies an override. The second argument controls the
preset itself. Use its typed options, such as `enableBffRequestId: false`,
`enableTelemetry: false`, `enableTelemetryExporters: false`, and
`enableModuleFederationSSR: false`, to turn preset features off.

Composition follows the Modern.js config merge rules:

- Nested records merge, so changing one nested field keeps its preset
  siblings.
- Scalars and `false` supplied by the app override preset values. Omitting a
  field, or leaving it `undefined`, keeps the preset value.
- Arrays and config hooks compose in preset-first order. Non-function array
  entries are deduplicated by deep equality, while function entries are all
  retained. An empty array or empty record does not clear values already
  supplied by the preset.

There is no generic reset sentinel. Use a typed preset option for preset-owned
behavior and a normal app config override for app-owned behavior.

`createPresetUltramodernConfig(options)` is an advanced API for inspecting or
materializing a fresh preset config. Do not spread its result into another
config object. Object spread is shallow, so replacing `server`, `output`, or
another nested record can silently discard preset siblings. Use
`presetUltramodern(...)` for authoring `modern.config.ts`.

### Bare preset and generated workspace policy

The preset and the workspace generator own different parts of the config:

| Source                                        | Identity                                                     | Remaining config                                                                                            |
| --------------------------------------------- | ------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------- |
| Bare `createPresetUltramodernConfig()` result | Uses `appId: 'app'` and has no `deliveryUnit`                | Uses the preset defaults. Telemetry stays enabled, but no exporter is configured until its endpoint is set. |
| Generated app                                 | Supplies the app-specific `appId` and stamped `deliveryUnit` | Inherits the other strict defaults from `presetUltramodern(...)`.                                           |
| Application                                   | Supplies the first `appConfig` argument                      | Overrides app-owned fields through the normal Modern.js merge.                                              |

Keep the generated `appId` and `deliveryUnit` options intact. To change
preset-owned behavior, use typed options such as
`enableTelemetryExporters: false`,
`enableTelemetry: false`, `enableBffRequestId: false`, or
`enableModuleFederationSSR: false`. To change an app-owned field, put the
override in the first argument, as in the example above.

Telemetry exporter endpoints are unset by default:

- `MODERN_TELEMETRY_OTLP_ENDPOINT` enables only the OTLP exporter.
- `MODERN_TELEMETRY_VICTORIA_ENDPOINT` enables only the VictoriaMetrics
  exporter.
- Setting both enables both. Leaving both unset keeps the telemetry pipeline
  enabled without configuring an exporter.

The typed `otlpEndpoint` and `victoriaMetricsEndpoint` preset options provide
the same per-exporter behavior without environment variables.

```bash
MODERN_TELEMETRY_OTLP_ENDPOINT=https://otel.example.com/v1/logs \
  mise exec -- pnpm dev
```

## Human Workflow

The public BleedingDev create package has one supported generated product. The
default command creates a production-ready UltraModern SuperApp workspace with
`presetUltramodern(...)`, SSR, TanStack Router, Tailwind CSS v4, i18n, Effect
BFF, Module Federation topology, generated quality gates, and Cloudflare deploy
basics:

```bash
pnpm dlx @bleedingdev/modern-js-ultramodern-create myapp
cd myapp
mise install
mise exec -- pnpm install
mise exec -- pnpm check
```

The workspace starts from a shell and generated platform contracts. It does not
generate a demo domain by default. Add real business MicroVerticals when they
become real ownership boundaries:

```bash
pnpm dlx @bleedingdev/modern-js-ultramodern-create transportation --vertical
pnpm dlx @bleedingdev/modern-js-ultramodern-create food-delivery --vertical
pnpm dlx @bleedingdev/modern-js-ultramodern-create payments --vertical
pnpm dlx @bleedingdev/modern-js-ultramodern-create maps --vertical
mise exec -- pnpm check
```

The `--vertical` command mutates the current workspace. It creates the vertical
package and updates topology metadata, ownership records, shell Module
Federation wiring, local development overlays, package dependencies, generated
contracts, ports, route-owned i18n, CSS isolation, and the vertical-owned Effect
BFF/client surface.

### Generator Automation

Automation can use explicit MicroVertical syntax instead of relying on the
positional form:

```bash
pnpm dlx @bleedingdev/modern-js-ultramodern-create --vertical=transportation
pnpm dlx @bleedingdev/modern-js-ultramodern-create --vertical-name transportation
pnpm dlx @bleedingdev/modern-js-ultramodern-create transportation --vertical --dry-run
pnpm dlx @bleedingdev/modern-js-ultramodern-create transportation --vertical \
  --codesmith-overlay ./generators/vertical-overlay
```

The BleedingDev package defaults to installing the published package cohort.
Use `--workspace` or `--ultramodern-package-source=workspace` for local monorepo
testing. Release proof and package-source automation can also pass
`--ultramodern-package-source=install`,
`--ultramodern-package-version`, `--ultramodern-package-registry`,
`--ultramodern-package-scope`, and
`--ultramodern-package-name-prefix`.

The supported public generator import is:

```ts
import {
  addUltramodernVertical,
  generateUltramodernWorkspace,
  planUltramodernVertical,
} from '@modern-js/ultramodern-create/ultramodern-workspace';

const workspace = generateUltramodernWorkspace({
  targetDir: '/tmp/my-workspace',
  packageName: 'my-workspace',
  modernVersion: '3.4.0',
});

const plan = planUltramodernVertical({
  workspaceRoot: workspace.workspaceRoot,
  name: 'transportation',
  modernVersion: '3.4.0',
});

addUltramodernVertical({
  workspaceRoot: workspace.workspaceRoot,
  name: 'transportation',
  modernVersion: '3.4.0',
  overlays: [{ generator: './generators/vertical-overlay' }],
});
```

The public result includes the workspace root, package source, created apps,
created paths, rewritten paths, assigned ports, Module Federation names, API
prefixes, generated contract path, and warnings. MicroVertical dry-run
returns the same shape plus `dryRun`, `selectedPort`, `moduleFederationRemote`,
`apiPrefix`, `jsonMutations`, `shellDependencyChanges`, and
`generatedContractChanges`; it prints JSON from the CLI and writes no files.

CodeSmith consumers can use the adapter subpath:

```ts
import ultramodernCodeSmith from '@modern-js/ultramodern-create/ultramodern-workspace/codesmith';

await ultramodernCodeSmith({
  config: {
    mode: 'vertical',
    name: 'transportation',
    workspaceRoot: process.cwd(),
    dryRun: true,
    logResult: true,
  },
});
```

Validation runs before the first write and reports the owning contract when a
vertical name is missing or invalid, an existing topology already owns the app
ID/path/package suffix/port/Module Federation name/API prefix/manifest key, a
workspace JSON contract is missing or not an object, or a Tailwind prefix would
collide. Fix the workspace contract or choose a different vertical name before
rerunning.

Overlays are explicit CodeSmith generators that run after base workspace or
MicroVertical generation. They extend generated output; they do not replace,
inherit, or shadow the base templates. The generator and CodeSmith adapter are
plain Node generator surfaces. The package build emits declaration files through
the TS-Go toolchain. Generated app packages keep stable `typescript` on TS7 so
Module Federation DTS generation and TS-Go use the same compiler lane; runtime
code does not depend on compiler internals.

### Runtime Contracts

Generated apps and verticals keep SSR on the normal Modern.js path. SuperApp
workspaces add `server.ssr.moduleFederationAppSSR` when Module Federation SSR is
needed, but the flag remains an explicit contract rather than a requirement for
every app.

Each app emits `src/routes/ultramodern-route-metadata` with
`ultramodernLocalisedUrls`. The i18n plugin reads that map in
`localeDetection.localisedUrls`; this is the explicit non-empty map that enables
translated path segments on top of `localePathRedirect`. The i18n plugin also
serves dynamic backend JSON from `/locales/{{lng}}/{{ns}}.json`. The route owner
changes localized paths and locale resource JSON together.

JSON-LD is optional route metadata, not inferred output. Private and
non-indexable routes emit no JSON-LD by default. Public route owners can add
`jsonLd` beside localized paths and use the generated
`src/routes/ultramodern-jsonld.ts` helpers for common schema.org shapes.

The generated contract writes `.modernjs/ultramodern.json` with a
`cssFederation` section:

- `packages/shared-design-tokens` owns the shared token layer and exports `./tokens.css`.
- Shell CSS owns only shell base and overlay layers under `[data-app-id="shell-super-app"]`.
- Each vertical owns one CSS layer, for example `[data-app-id="vertical-transportation"]` with app-local class prefixes.
- Tailwind CSS v4 is local to each generated app through `@rsbuild/plugin-tailwindcss`; shared base styles must not be duplicated by verticals.
- SSR first paint requires shared token CSS and app-owned CSS to be emitted by Modern/Rspack assets. Vertical CSS is loaded through manifest ownership, not copied into shell source.

Version switching must select UI, API, CSS, i18n JSON, and MF manifest evidence from the same vertical build marker. A shell render that only changes the UI marker is not enough.

### Validation And Deploy

Local gates:

```bash
mise exec -- pnpm check
mise exec -- pnpm build
mise exec -- pnpm cloudflare:build
```

Generated workspaces include `scripts/proof-cloudflare-version.mjs` for live
Cloudflare and Zephyr proof:

```bash
ULTRAMODERN_PUBLIC_URL_SHELL_SUPER_APP=https://shell-super-app.example.workers.dev \
ULTRAMODERN_PUBLIC_URL_TRANSPORTATION=https://transportation.example.workers.dev \
ULTRAMODERN_PUBLIC_URL_PAYMENTS=https://payments.example.workers.dev \
mise exec -- pnpm cloudflare:proof --require-public-urls
```

Live Cloudflare and Zephyr proof requires public Worker URLs and Zephyr credentials. Without those, the repo can validate generated contracts, local builds, local Cloudflare output, dry-run Zephyr evidence plans, and local evidence schemas, but it cannot prove shell-driven live version selection.

BleedingDev packages are published through GitHub Actions trusted publishing.
The public workflow is tokenless; do not publish packages manually from a
developer machine.

## Related Docs

- [BFF Runtime Frameworks](/guides/advanced-features/bff/frameworks.md)
- [BFF Data Platform](/guides/advanced-features/bff/data-platform.md)
- [BFF Effect Runtime Config](/configure/app/bff/effect.md)
- [Module Federation SSR](/guides/topic-detail/module-federation/ssr.md)
- [performance.rsdoctor](/configure/app/performance/rsdoctor.md)
- [server.ssr](/configure/app/server/ssr.md)
