Skip to content

Update react-router-dom 7.2.0 → 7.3.0 (minor) #299

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Mar 19, 2025

Conversation

depfu[bot]
Copy link

@depfu depfu bot commented Mar 18, 2025

Here is everything you need to know about this upgrade. Please take a good look at what changed and the test results before merging this pull request.

What changed?

✳️ react-router-dom (7.2.0 → 7.3.0) · Repo · Changelog

Release Notes

7.3.0 (from changelog)

Date: 2025-03-06

Minor Changes

  • Add fetcherKey as a parameter to patchRoutesOnNavigation (#13061)

Patch Changes

  • react-router - Detect and handle manifest-skew issues on new deploys during active sessions (#13061)
    • In framework mode, Lazy Route Discovery will now detect manifest version mismatches in active sessions after a new deploy
    • On navigations to undiscovered routes, this mismatch will trigger a document reload of the destination path
    • On fetcher calls to undiscovered routes, this mismatch will trigger a document reload of the current path
  • react-router - Skip resource route flow in dev server in SPA mode (#13113)
  • react-router - Fix single fetch _root.data requests when a basename is used (#12898)
  • react-router - Fix types for loaderData and actionData that contained Records (#13139)
    • ⚠️ This is a breaking change for users who have already adopted unstable_SerializesTo - see the note in the Unstable Changes section below for more information
  • @react-router/dev - Fix support for custom client build.rollupOptions.output.entryFileNames (#13098)
  • @react-router/dev - Fix usage of prerender option when serverBundles option has been configured or provided by a preset, e.g. vercelPreset from @vercel/react-router (#13082)
  • @react-router/dev - Fix support for custom build.assetsDir (#13077)
  • @react-router/dev - Remove unused dependencies (#13134)
  • @react-router/dev - Stub all routes except root in "SPA Mode" server builds to avoid issues when route modules or their dependencies import non-SSR-friendly modules (#13023)
  • @react-router/dev - Remove unused Vite file system watcher (#13133)
  • @react-router/dev - Fix support for custom SSR build input when serverBundles option has been configured (#13107)
    • ⚠️ Note that for consumers using the future.unstable_viteEnvironmentApi and serverBundles options together, hyphens are no longer supported in server bundle IDs since they also need to be valid Vite environment names.
  • @react-router/dev - Fix dev server when using HTTPS by stripping HTTP/2 pseudo headers from dev server requests (#12830)
  • @react-router/dev - Lazy load Cloudflare platform proxy on first dev server request when using the cloudflareDevProxy Vite plugin to avoid creating unnecessary workerd processes (#13016)
  • @react-router/dev - Fix duplicated entries in typegen for layout routes and their corresponding index route (#13140)
  • @react-router/express - Update express peerDependency to include v5 (#13064) (#12961)

Unstable Changes

⚠️ Unstable features are not recommended for production use

  • react-router - Add context support to client side data routers (unstable) (#12941)
  • react-router - Support middleware on routes (unstable) (#12941)
  • @react-router/dev - Fix errors with future.unstable_viteEnvironmentApi when the ssr environment has been configured by another plugin to be a custom Vite.DevEnvironment rather than the default Vite.RunnableDevEnvironment (#13008)
  • @react-router/dev - When future.unstable_viteEnvironmentApi is enabled and the ssr environment has optimizeDeps.noDiscovery disabled, define optimizeDeps.entries and optimizeDeps.include (#13007)

Client-side context (unstable)

Your application clientLoader/clientAction functions (or loader/action in library mode) will now receive a context parameter on the client. This is an instance of unstable_RouterContextProvider that you use with type-safe contexts (similar to React.createContext) and is most useful with the corresponding unstable_clientMiddleware API:

import { unstable_createContext } from "react-router";

type User = {
/.../
};

const userContext = unstable_createContext<User>();

const sessionMiddleware: Route.unstable_ClientMiddlewareFunction = async ({
context,
}) => {
let user = await getUser();
context.set(userContext, user);
};

export const unstable_clientMiddleware = [sessionMiddleware];

export function clientLoader({ context }: Route.ClientLoaderArgs) {
let user = context.get(userContext);
let profile = await getProfile(user.id);
return { profile };
}

Similar to server-side requests, a fresh context will be created per navigation (or fetcher call). If you have initial data you'd like to populate in the context for every request, you can provide an unstable_getContext function at the root of your app:

  • Library mode - createBrowserRouter(routes, { unstable_getContext })
  • Framework mode - <HydratedRouter unstable_getContext>

This function should return an value of type unstable_InitialContext which is a Map<unstable_RouterContext, unknown> of context's and initial values:

const loggerContext = unstable_createContext<(...args: unknown[]) => void>();

function logger(...args: unknown[]) {
console.log(new Date.toISOString(), ...args);
}

function unstable_getContext() {
let map = new Map();
map.set(loggerContext, logger);
return map;
}

Middleware (unstable)

Middleware is implemented behind a future.unstable_middleware flag. To enable, you must enable the flag and the types in your react-router-config.ts file:

import type { Config } from "@react-router/dev/config";
import type { Future } from "react-router";

declare module "react-router" {
interface Future {
unstable_middleware: true; // 👈 Enable middleware types
}
}

export default {
future: {
unstable_middleware: true, // 👈 Enable middleware
},
} satisfies Config;

⚠️ Middleware is unstable and should not be adopted in production. There is at least one known de-optimization in route module loading for clientMiddleware that we will be addressing this before a stable release.

⚠️ Enabling middleware contains a breaking change to the context parameter passed to your loader/action functions - see below for more information.

Once enabled, routes can define an array of middleware functions that will run sequentially before route handlers run. These functions accept the same parameters as loader/action plus an additional next parameter to run the remaining data pipeline. This allows middlewares to perform logic before and after handlers execute.

// Framework mode
export const unstable_middleware = [serverLogger, serverAuth]; // server
export const unstable_clientMiddleware = [clientLogger]; // client

// Library mode
const routes = [
{
path: "/",
// Middlewares are client-side for library mode SPA's
unstable_middleware: [clientLogger, clientAuth],
loader: rootLoader,
Component: Root,
},
];

Here's a simple example of a client-side logging middleware that can be placed on the root route:

const clientLogger: Route.unstable_ClientMiddlewareFunction = async (
{ request },
next
) => {
let start = performance.now();

// Run the remaining middlewares and all route loaders
await next();

let duration = performance.now() - start;
console.log(Navigated to <span class="pl-s1"><span class="pl-kos">${</span><span class="pl-s1">request</span><span class="pl-kos">.</span><span class="pl-c1">url</span><span class="pl-kos">}</span></span> (<span class="pl-s1"><span class="pl-kos">${</span><span class="pl-s1">duration</span><span class="pl-kos">}</span></span>ms));
};

Note that in the above example, the next/middleware functions don't return anything. This is by design as on the client there is no "response" to send over the network like there would be for middlewares running on the server. The data is all handled behind the scenes by the stateful router.

For a server-side middleware, the next function will return the HTTP Response that React Router will be sending across the wire, thus giving you a chance to make changes as needed. You may throw a new response to short circuit and respond immediately, or you may return a new or altered response to override the default returned by next().

const serverLogger: Route.unstable_MiddlewareFunction = async (
{ request, params, context },
next
) => {
let start = performance.now();

// 👇 Grab the response here
let res = await next();

let duration = performance.now() - start;
console.log(Navigated to <span class="pl-s1"><span class="pl-kos">${</span><span class="pl-s1">request</span><span class="pl-kos">.</span><span class="pl-c1">url</span><span class="pl-kos">}</span></span> (<span class="pl-s1"><span class="pl-kos">${</span><span class="pl-s1">duration</span><span class="pl-kos">}</span></span>ms));

// 👇 And return it here (optional if you don't modify the response)
return res;
};

You can throw a redirect from a middleware to short circuit any remaining processing:

import { sessionContext } from "../context";
const serverAuth: Route.unstable_MiddlewareFunction = (
  { request, params, context },
  next
) => {
  let session = context.get(sessionContext);
  let user = session.get("user");
  if (!user) {
    session.set("returnTo", request.url);
    throw redirect("/login", 302);
  }
};

Note that in cases like this where you don't need to do any post-processing you don't need to call the next function or return a Response.

Here's another example of using a server middleware to detect 404s and check the CMS for a redirect:

const redirects: Route.unstable_MiddlewareFunction = async ({
request,
next,
}) => {
// attempt to handle the request
let res = await next();

// if it's a 404, check the CMS for a redirect, do it last
// because it's expensive
if (res.status === 404) {
let cmsRedirect = await checkCMSRedirects(request.url);
if (cmsRedirect) {
throw redirect(cmsRedirect, 302);
}
}

return res;
};

For more information on the middleware API/design, please see the decision doc.

Middleware context parameter

When middleware is enabled, your application will use a different type of context parameter in your loaders and actions to provide better type safety. Instead of AppLoadContext, context will now be an instance of ContextProvider that you can use with type-safe contexts (similar to React.createContext):

import { unstable_createContext } from "react-router";
import { Route } from "./+types/root";
import type { Session } from "./sessions.server";
import { getSession } from "./sessions.server";

let sessionContext = unstable_createContext<Session>();

const sessionMiddleware: Route.unstable_MiddlewareFunction = ({
context,
request,
}) => {
let session = await getSession(request);
context.set(sessionContext, session);
// ^ must be of type Session
};

// ... then in some downstream middleware
const loggerMiddleware: Route.unstable_MiddlewareFunction = ({
context,
request,
}) => {
let session = context.get(sessionContext);
// ^ typeof Session
console.log(session.get("userId"), request.method, request.url);
};

// ... or some downstream loader
export function loader({ context }: Route.LoaderArgs) {
let session = context.get(sessionContext);
let profile = await getProfile(session.get("userId"));
return { profile };
}

If you are using a custom server with a getLoadContext function, the return value for initial context values passed from the server adapter layer is no longer an object and should now return an unstable_InitialContext (Map<RouterContext, unknown>):

let adapterContext = unstable_createContext<MyAdapterContext>();

function getLoadContext(req, res): unstable_InitialContext {
let map = new Map();
map.set(adapterContext, getAdapterContext(req));
return map;
}

unstable_SerializesTo

unstable_SerializesTo added a way to register custom serialization types in Single Fetch for other library and framework authors like Apollo. It was implemented with branded type whose branded property that was made optional so that casting arbitrary values was easy:

// without the brand being marked as optional
let x1 = 42 as unknown as unstable_SerializesTo<number>;
// ^^^^^^^^^^

// with the brand being marked as optional
let x2 = 42 as unstable_SerializesTo<number>;

However, this broke type inference in loaderData and actionData for any Record types as those would now (incorrectly) match unstable_SerializesTo. This affected all users, not just those that depended on unstable_SerializesTo. To fix this, the branded property of unstable_SerializesTo is marked as required instead of optional.

For library and framework authors using unstable_SerializesTo, you may need to add as unknown casts before casting to unstable_SerializesTo.

Changes by Package

Full Changelog: v7.2.0...v7.3.0

Does any of this look wrong? Please let us know.


Depfu Status

Depfu will automatically keep this PR conflict-free, as long as you don't add any commits to this branch yourself. You can also trigger a rebase manually by commenting with @depfu rebase.

All Depfu comment commands
@​depfu rebase
Rebases against your default branch and redoes this update
@​depfu recreate
Recreates this PR, overwriting any edits that you've made to it
@​depfu merge
Merges this PR once your tests are passing and conflicts are resolved
@​depfu cancel merge
Cancels automatic merging of this PR
@​depfu close
Closes this PR and deletes the branch
@​depfu reopen
Restores the branch and reopens this PR (if it's closed)
@​depfu pause
Ignores all future updates for this dependency and closes this PR
@​depfu pause [minor|major]
Ignores all future minor/major updates for this dependency and closes this PR
@​depfu resume
Future versions of this dependency will create PRs again (leaves this PR as is)

@depfu depfu bot added the dependencies Pull requests that update a dependency file label Mar 18, 2025
@depfu depfu bot requested review from canova and julienw March 18, 2025 20:25
Copy link

netlify bot commented Mar 18, 2025

Deploy Preview for firefox-devtools-react-contextmenu ready!

Name Link
🔨 Latest commit 5746359
🔍 Latest deploy log https://app.netlify.com/sites/firefox-devtools-react-contextmenu/deploys/67da9cd32461dc0008b5517e
😎 Deploy Preview https://deploy-preview-299--firefox-devtools-react-contextmenu.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
Lighthouse
Lighthouse
1 paths audited
Performance: 88
Accessibility: 82
Best Practices: 92
SEO: 92
PWA: -
View the detailed breakdown and full score reports

To edit notification comments on pull requests, go to your Netlify site configuration.

Copy link

@julienw julienw left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm

@julienw julienw enabled auto-merge (squash) March 19, 2025 10:30
@julienw julienw merged commit 3236acb into master Mar 19, 2025
6 checks passed
@depfu depfu bot deleted the depfu/update/yarn/react-router-dom-7.3.0 branch March 19, 2025 10:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
dependencies Pull requests that update a dependency file
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant