remix@3.0.0-beta.4
Breaking Changes📦 remixView on GitHub →
⚠ 2 breaking✨ 8 features🐛 2 fixes🔧 23 symbols
Summary
This pre-release introduces significant router enhancements, including route mounting capabilities and improved middleware contract enforcement, alongside better Node assertion compatibility.
⚠️ Breaking Changes
- Middleware consumed through `remix/router` and `remix/fetch-router` must now explicitly continue the request chain by calling `next()` or return a `Response`. Middleware that returned `undefined` without calling `next()` now throws at runtime instead of implicitly continuing. Fix by ensuring middleware returns `next()` or a `Response`.
- `MapTarget` and `MapHandler` are no longer re-exported from `remix/router` or `remix/fetch-router`. Use the public `Router`, `RouteBuilder`, `RouteInstaller`, `Action`, and `Controller` types instead.
Migration Steps
- Update context-loading middleware in `remix/router` or `remix/fetch-router` to explicitly return the downstream response, typically by calling `return next()`.
- Replace usage of re-exported `MapTarget` and `MapHandler` with the public `Router`, `RouteBuilder`, `RouteInstaller`, `Action`, and `Controller` types.
✨ New Features
- Added `router.mount()` and the `RouteBuilder`/`RouteInstaller` types to `remix/router` and `remix/fetch-router` to allow building larger apps from smaller route groups.
- Params from the mount pattern are available to mounted handlers; duplicate param names follow `route-pattern` behavior where the right-most param wins.
- `RouterContext<typeof router>` extracts the request context provided by a router or route builder, allowing apps to keep root middleware inline.
- `createAction()`, direct action objects, and `createController()` now infer middleware-provided values from plain inline middleware arrays, reducing the need for explicit generics.
- `createMiddleware()` creates reusable middleware chains that preserve their tuple type without `as const` across inference boundaries.
- The public type surface for re-exported router types is smaller, preserving route params, middleware context inference, and stored action/controller compatibility checks.
- Exposed `remix/test` timeout and abort signal support through the main Remix package; tests/lifecycle hooks can pass `{ timeout, signal }`, and `t.signal` aborts on timeout.
- String `skip`/`todo` reasons now flow through `remix/test` results and reporter output.
🐛 Bug Fixes
- Improved Node compatibility for the `remix/assert` entrypoint: default export is callable as `assert.ok` alias, failure errors expose Node-style metadata, expected-error/message handling aligns closer to `node:assert/strict`, strict/deep equality handles `Object.is` and built-in object comparisons more consistently with Node.
- Kept generated README mirrors in the published `remix` package so documentation remains available at `node_modules/remix/src/<subpath>/README.md`.
Affected Symbols
remix/routerremix/fetch-routernext()Responserouter.mount()RouteBuilderRouteInstallerRouterContextRouterTypes.contextcreateAction()createController()MiddlewareContextcreateMiddleware()MiddlewarecreateRouter()router.map()MapTargetMapHandlerremix/assertassert.okassert.partialDeepEqualremix/testt.signal