github remix-run/remix remix@3.0.0-beta.4
remix v3.0.0-beta.4

10 hours ago

Pre-release Changes

  • BREAKING CHANGE: 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.

    Update context-loading middleware to return the downstream response:

    function loadUser(): Middleware {
      return (context, next) => {
        context.set(CurrentUser, user)
        return next()
      }
    }
  • Improve Node compatibility for the remix/assert entrypoint.

    The default export is now callable as an alias for assert.ok, failure errors expose Node-style metadata, expected-error/message handling more closely follows node:assert/strict, strict/deep equality now handles Object.is and built-in object comparisons more consistently with Node, and assert.partialDeepEqual is available for strict partial deep comparisons.

  • Add router.mount() and the RouteBuilder/RouteInstaller types to remix/router and remix/fetch-router so larger apps can be built from smaller route groups without making feature modules hard-code their final URL. A feature module can now export a local route installer that registers routes like /, /users/:id, or a route map, and the parent can mount it at /admin, /orgs/:orgId, or another route pattern prefix while one router still owns request dispatch, matching, middleware, and default handling. Params from the mount pattern are available to mounted handlers, and 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, so apps can keep root middleware inline and use that router-derived context for RouterTypes.context. createAction(), direct action objects, and createController() now infer middleware-provided values from plain inline middleware arrays, which removes the usual need for intermediate MiddlewareContext<...> aliases and explicit generics in stored actions and controllers.

    createMiddleware() creates reusable middleware chains that preserve their tuple type without as const in the specific cases where a chain crosses an inference boundary: deriving MiddlewareContext<typeof rootMiddleware> without a router value, exporting a reusable chain, or returning a chain from a factory. Prefer inline arrays for ordinary middleware options on routers, controllers, actions, and route helpers. Middleware is now a callable type alias with type-only context metadata, which preserves inline middleware context transforms more reliably than an interface call signature.

    The re-exported router types also keep createRouter() and router.map() to single call signatures while preserving route params, middleware context inference, and stored action/controller compatibility checks, making the public type surface smaller while everyday route setup gets more expressive.

    BREAKING CHANGE: 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.

  • Expose remix/test timeout and abort signal support through the Remix package.

    Tests and lifecycle hooks can pass { timeout, signal }, and t.signal aborts when a test times out. String skip/todo reasons now flow through remix/test results and reporter output.

  • Keep generated README mirrors in the published remix package so node_modules/remix/src/<subpath>/README.md documentation remains available while the duplicated source files stay untracked in git.

  • Bumped @remix-run/* dependencies:

Don't miss a new remix release

NewReleases is sending notifications on new releases.