Skip to content

Update react-router-dom 6.28.0 → 7.0.2 (major) #266

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

Closed
wants to merge 1 commit into from

Conversation

depfu[bot]
Copy link

@depfu depfu bot commented Dec 10, 2024

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 (6.28.0 → 7.0.2) · Repo · Changelog

Release Notes

7.0.2 (from changelog)

Date: 2024-12-02

Patch Changes

  • Temporarily only use one build in export map so packages can have a peer dependency on react router (#12437)

  • Support moduleResolution Node16 and NodeNext (#12440)

  • Generate wide matches and params types for child routes (#12397)

    At runtime, matches includes child route matches and params include child route path parameters. But previously, we only generated types for parent routes and the current route in matches and params. To align our generated types more closely to the runtime behavior, we now generate more permissive, wider types when accessing child route information.

Changes by Package

Full Changelog: v7.0.1...v7.0.2

7.0.1 (from changelog)

Date: 2024-11-22

Patch Changes

  • Ensure typegen file watcher is cleaned up when Vite dev server restarts (#12331)
  • Pass route error to ErrorBoundary as a prop (#12338)

Changes by Package

Full Changelog: v7.0.0...v7.0.1

7.0.0 (from changelog)

Date: 2024-11-21

Breaking Changes

Package Restructuring

  • The react-router-dom, @remix-run/react, @remix-run/server-runtime, and @remix-run/router have been collapsed into the react-router package
    • To ease migration, react-router-dom is still published in v7 as a re-export of everything from react-router
  • The @remix-run/cloudflare-pages and @remix-run/cloudflare-workers have been collapsed into @react-router/cloudflare package`
  • The react-router-dom-v5-compat and react-router-native packages are removed starting with v7

Removed Adapter Re-exports

Remix v2 used to re-export all common @remix-run/server-runtime APIs through the various runtime packages (node, cloudflare, deno) so that you wouldn't need an additional @remix-run/server-runtime dependency in your package.json. With the collapsing of packages into react-router, these common APIs are now no longer re-exported through the runtime adapters. You should import all common APIs from react-router, and only import runtime-specific APIs from the runtime packages:

// Runtime-specific APIs
import { createFileSessionStorage } from "@react-router/node";
// Runtime-agnostic APIs
import { redirect, useLoaderData } from "react-router";

Removed APIs

The following APIs have been removed in React Router v7:

  • json
  • defer
  • unstable_composeUploadHandlers
  • unstable_createMemoryUploadHandler
  • unstable_parseMultipartFormData

Minimum Versions

React Router v7 requires the following minimum versions:

  • node@20
    • React Router no longer provides an installGlobals method to polyfill the fetch API
  • react@18, react-dom@18

Adopted Future Flag Behaviors

Remix and React Router follow an API Development Strategy leveraging "Future Flags" to avoid introducing a slew of breaking changes in a major release. Instead, breaking changes are introduce din minor releases behind a flag, allowing users to opt-in at their convenience. In the next major release, all future flag behaviors become the default behavior.

The following previously flagged behaviors are now the default in React Router v7:

  • React Router v6 flags
    • future.v7_relativeSplatPath
    • future.v7_startTransition
    • future.v7_fetcherPersist
    • future.v7_normalizeFormMethod
    • future.v7_partialHydration
    • future.v7_skipActionStatusRevalidation
  • Remix v2 flags
    • future.v3_fetcherPersist
    • future.v3_relativeSplatPath
    • future.v3_throwAbortReason
    • future.v3_singleFetch
    • future.v3_lazyRouteDiscovery
    • future.v3_optimizeDeps

Vite Compiler

The Remix Vite plugin is the proper way to build full-stack SSR apps using React Router v7. The former esbuild-based compiler is no longer available.

Renamed vitePlugin and cloudflareDevProxyVitePlugin

For Remix consumers migrating to React Router, the vitePlugin and cloudflareDevProxyVitePlugin exports have been renamed and moved (#11904)

-import {
- vitePlugin as remix,
- cloudflareDevProxyVitePlugin,
-} from "@remix/dev";

+import { reactRouter } from "@react-router/dev/vite";
+import { cloudflareDevProxy } from "@react-router/dev/vite/cloudflare";

Removed manifest option

For Remix consumers migrating to React Router, the Vite plugin's manifest option has been removed. The manifest option been superseded by the more powerful buildEnd hook since it's passed the buildManifest argument. You can still write the build manifest to disk if needed, but you'll most likely find it more convenient to write any logic depending on the build manifest within the buildEnd hook itself. (#11573)

If you were using the manifest option, you can replace it with a buildEnd hook that writes the manifest to disk like this:

// react-router.config.ts
import { type Config } from "@react-router/dev/config";
import { writeFile } from "node:fs/promises";

export default {
async buildEnd({ buildManifest }) {
await writeFile(
"build/manifest.json",
JSON.stringify(buildManifest, null, 2),
"utf-8"
);
},
} satisfies Config;

Exposed Router Promises

Because React 19 will have first-class support for handling promises in the render pass (via React.use and useAction), we are now comfortable exposing the promises for the APIs that previously returned undefined:

  • useNavigate()
  • useSubmit()
  • useFetcher().load
  • useFetcher().submit
  • useRevalidator().revalidate()

Other Notable Changes

routes.ts

When using the React Router Vite plugin, routes are defined in app/routes.ts. Route config is exported via the routes export, conforming to the RouteConfig type. Route helper functions route, index, and layout are provided to make declarative type-safe route definitions easier.

// app/routes.ts
import {
type RouteConfig,
route,
index,
layout,
} from "@react-router/dev/routes";

export const routes: RouteConfig = [
index("./home.tsx"),
route("about", "./about.tsx"),

layout("./auth/layout.tsx", [
route("login", "./auth/login.tsx"),
route("register", "./auth/register.tsx"),
]),

route("concerts", [
index("./concerts/home.tsx"),
route(":city", "./concerts/city.tsx"),
route("trending", "./concerts/trending.tsx"),
]),
];

For Remix consumers migrating to React Router, you can still configure file system routing within routes.ts using the @react-router/fs-routes package. A minimal route config that reproduces the default Remix setup looks like this:

// app/routes.ts
import { type RouteConfig } from "@react-router/dev/routes";
import { flatRoutes } from "@react-router/fs-routes";

export const routes: RouteConfig = flatRoutes();

If you want to migrate from file system routing to config-based routes, you can mix and match approaches by spreading the results of the async flatRoutes function into the array of config-based routes.

// app/routes.ts
import { type RouteConfig, route } from "@react-router/dev/routes";
import { flatRoutes } from "@react-router/fs-routes";

export const routes: RouteConfig = [
// Example config-based route:
route("/hello", "./routes/hello.tsx"),

// File system routes scoped to a different directory:
...(await flatRoutes({
rootDirectory: "fs-routes",
})),
];

If you were using Remix's routes option to use alternative file system routing conventions, you can adapt these to the new RouteConfig format using @react-router/remix-config-routes-adapter.

For example, if you were using Remix v1 route conventions in Remix v2, you can combine @react-router/remix-config-routes-adapter with @remix-run/v1-route-convention to adapt this to React Router:

// app/routes.ts
import { type RouteConfig } from "@react-router/dev/routes";
import { remixConfigRoutes } from "@react-router/remix-config-routes-adapter";
import { createRoutesFromFolders } from "@remix-run/v1-route-convention";

export const routes: RouteConfig = remixConfigRoutes(async (defineRoutes) => {
return createRoutesFromFolders(defineRoutes, {
ignoredFilePatterns: ["/.*", "/*.css"],
});
});

Also note that, if you were using Remix's routes option to define config-based routes, you can also adapt these to the new RouteConfig format using @react-router/remix-config-routes-adapter with minimal code changes. While this makes for a fast migration path, we recommend migrating any config-based routes from Remix to the new RouteConfig format since it's a fairly straightforward migration.

// app/routes.ts
-import { type RouteConfig } from "@react-router/dev/routes";
+import { type RouteConfig, route } from "@react-router/dev/routes";
-import { remixConfigRoutes } from "@react-router/remix-config-routes-adapter";

-export const routes: RouteConfig = remixConfigRoutes(async (defineRoutes) => {
- defineRoutes((route) => {
- route("/parent", "./routes/parent.tsx", () => [
- route("/child", "./routes/child.tsx"),
- ]);
- });
-});
+export const routes: RouteConfig = [
+ route("/parent", "./routes/parent.tsx", [
+ route("/child", "./routes/child.tsx"),
+ ]),
+];

Typesafety improvements

React Router now generates types for each of your route modules and passes typed props to route module component exports (#11961, #12019). You can access those types by importing them from ./+types/<route filename without extension>.

See How To > Route Module Type Safety and Explanations > Type Safety for more details.

Prerendering

React Router v7 includes a new prerender config in the vite plugin to support SSG use-cases. This will pre-render your .html and .data files at build time and so you can serve them statically at runtime from a running server or a CDN (#11539)

export default defineConfig({
plugins: [
reactRouter({
async prerender({ getStaticPaths }) {
let slugs = await fakeGetSlugsFromCms();
return [
...getStaticPaths(),
...slugs.map((slug) => /product/<span class="pl-s1"><span class="pl-kos">${</span><span class="pl-s1">slug</span><span class="pl-kos">}</span></span>),
];
},
}),
tsconfigPaths(),
],
});

async function fakeGetSlugsFromCms() {
await new Promise((r) => setTimeout(r, 1000));
return ["shirt", "hat"];
}

Major Changes (react-router)

  • Remove the original defer implementation in favor of using raw promises via single fetch and turbo-stream (#11744)
    • This removes these exports from React Router:
      • defer
      • AbortedDeferredError
      • type TypedDeferredData
      • UNSAFE_DeferredData
      • UNSAFE_DEFERRED_SYMBOL
  • Collapse packages into react-router(#11505)
    • @remix-run/router
    • react-router-dom
    • @remix-run/server-runtime
    • @remix-run/testing
    • As a note, the react-router-dom package is maintained to ease adoption but it simply re-exports all APIs from react-router
  • Drop support for Node 16, React Router SSR now requires Node 18 or higher (#11391, #11690)
  • Remove future.v7_startTransition flag (#11696)
  • Expose the underlying router promises from the following APIs for composition in React 19 APIs: (#11521)
  • Remove future.v7_normalizeFormMethod future flag (#11697)
  • Imports/Exports cleanup (#11840)
    • Removed the following exports that were previously public API from @remix-run/router
      • types
        • AgnosticDataIndexRouteObject
        • AgnosticDataNonIndexRouteObject
        • AgnosticDataRouteMatch
        • AgnosticDataRouteObject
        • AgnosticIndexRouteObject
        • AgnosticNonIndexRouteObject
        • AgnosticRouteMatch
        • AgnosticRouteObject
        • TrackedPromise
        • unstable_AgnosticPatchRoutesOnMissFunction
        • Action -> exported as NavigationType via react-router
        • Router exported as RemixRouter to differentiate from RR's <Router>
      • API
        • getToPathname (@private)
        • joinPaths (@private)
        • normalizePathname (@private)
        • resolveTo (@private)
        • stripBasename (@private)
        • createBrowserHistory -> in favor of createBrowserRouter
        • createHashHistory -> in favor of createHashRouter
        • createMemoryHistory -> in favor of createMemoryRouter
        • createRouter
        • createStaticHandler -> in favor of wrapper createStaticHandler in RR Dom
        • getStaticContextFromError
    • Removed the following exports that were previously public API from react-router
      • Hash
      • Pathname
      • Search
  • Remove future.v7_prependBasename from the internalized @remix-run/router package (#11726)
  • Remove future.v7_throwAbortReason from internalized @remix-run/router package (#11728)
  • Add exports field to all packages (#11675)
  • Renamed RemixContext to FrameworkContext (#11705)
  • Update the minimum React version to 18 (#11689)
  • PrefetchPageDescriptor replaced by PageLinkDescriptor (#11960)
  • Remove the future.v7_partialHydration flag (#11725)
    • This also removes the <RouterProvider fallbackElement> prop
      • To migrate, move the fallbackElement to a hydrateFallbackElement/HydrateFallback on your root route
    • Also worth nothing there is a related breaking changer with this future flag:
      • Without future.v7_partialHydration (when using fallbackElement), state.navigation was populated during the initial load
      • With future.v7_partialHydration, state.navigation remains in an "idle" state during the initial load
  • Remove future.v7_relativeSplatPath future flag (#11695)
  • Remove remaining future flags (#11820)
    • React Router v7_skipActionErrorRevalidation
    • Remix v3_fetcherPersist, v3_relativeSplatPath, v3_throwAbortReason
  • Rename createRemixStub to createRoutesStub (#11692)
  • Remove @remix-run/router deprecated detectErrorBoundary option in favor of mapRouteProperties (#11751)
  • Add react-router/dom subpath export to properly enable react-dom as an optional peerDependency (#11851)
    • This ensures that we don't blindly import ReactDOM from "react-dom" in <RouterProvider> in order to access ReactDOM.flushSync(), since that would break createMemoryRouter use cases in non-DOM environments
    • DOM environments should import from react-router/dom to get the proper component that makes ReactDOM.flushSync() available:
      • If you are using the Vite plugin, use this in your entry.client.tsx:
        • import { HydratedRouter } from 'react-router/dom'
      • If you are not using the Vite plugin and are manually calling createBrowserRouter/createHashRouter:
        • import { RouterProvider } from "react-router/dom"
  • Remove future.v7_fetcherPersist flag (#11731)
  • Allow returning undefined from loaders and actions (#11680, #12057)
  • Use createRemixRouter/RouterProvider in entry.client instead of RemixBrowser (#11469)
  • Remove the deprecated json utility (#12146)
    • You can use Response.json if you still need to construct JSON responses in your app

Major Changes (@react-router/*)

  • Remove future.v3_singleFetch flag (#11522)
  • Drop support for Node 16 and 18, update minimum Node version to 20 (#11690, #12171)
    • Remove installGlobals() as this should no longer be necessary
  • Add exports field to all packages (#11675)
  • No longer re-export APIs from react-router through different runtime/adapter packages (#11702)
  • For Remix consumers migrating to React Router, the crypto global from the Web Crypto API is now required when using cookie and session APIs
    • This means that the following APIs are provided from react-router rather than platform-specific packages: (#11837)
      • createCookie
      • createCookieSessionStorage
      • createMemorySessionStorage
      • createSessionStorage
    • For consumers running older versions of Node, the installGlobals function from @remix-run/node has been updated to define globalThis.crypto, using Node's require('node:crypto').webcrypto implementation
    • Since platform-specific packages no longer need to implement this API, the following low-level APIs have been removed:
      • createCookieFactory
      • createSessionStorageFactory
      • createCookieSessionStorageFactory
      • createMemorySessionStorageFactory
  • Consolidate types previously duplicated across @remix-run/router, @remix-run/server-runtime, and @remix-run/react now that they all live in react-router (#12177)
    • Examples: LoaderFunction, LoaderFunctionArgs, ActionFunction, ActionFunctionArgs, DataFunctionArgs, RouteManifest, LinksFunction, Route, EntryRoute
    • The RouteManifest type used by the "remix" code is now slightly stricter because it is using the former @remix-run/router RouteManifest
      • Record<string, Route> -> Record<string, Route | undefined>
    • Removed AppData type in favor of inlining unknown in the few locations it was used
    • Removed ServerRuntimeMeta* types in favor of the Meta* types they were duplicated from
  • Migrate Remix v2 type generics to React Router (#12180)
    • These generics are provided for Remix v2 migration purposes
    • These generics and the APIs they exist on should be considered informally deprecated in favor of the new Route.* types
    • Anyone migrating from React Router v6 should probably not leverage these new generics and should migrate straight to the Route.* types
    • For React Router v6 users, these generics are new and should not impact your app, with one exception
      • useFetcher previously had an optional generic (used primarily by Remix v2) that expected the data type
      • This has been updated in v7 to expect the type of the function that generates the data (i.e., typeof loader/typeof action)
      • Therefore, you should update your usages:
        • useFetcher<LoaderData>()
        • useFetcher<typeof loader>()
  • Update cookie dependency to ^1.0.1 - please see the release notes for any breaking changes (#12172)
  • @react-router/cloudflare - For Remix consumers migrating to React Router, all exports from @remix-run/cloudflare-pages are now provided for React Router consumers in the @react-router/cloudflare package. There is no longer a separate package for Cloudflare Pages. (#11801)
  • @react-router/cloudflare - The @remix-run/cloudflare-workers package has been deprecated. Remix consumers migrating to React Router should use the @react-router/cloudflare package directly. For guidance on how to use @react-router/cloudflare within a Cloudflare Workers context, refer to the Cloudflare Workers template. (#11801)
  • @react-router/dev - For Remix consumers migrating to React Router, the vitePlugin and cloudflareDevProxyVitePlugin exports have been renamed and moved. (#11904)
  • @react-router/dev - For Remix consumers migrating to React Router who used the Vite plugin's buildEnd hook, the resolved reactRouterConfig object no longer contains a publicPath property since this belongs to Vite, not React Router (#11575)
  • @react-router/dev - For Remix consumers migrating to React Router, the Vite plugin's manifest option has been removed (#11573)
  • @react-router/dev - Update default isbot version to v5 and drop support for isbot@3 (#11770)
    • If you have isbot@4 or isbot@5 in your package.json:
      • You do not need to make any changes
    • If you have isbot@3 in your package.json and you have your own entry.server.tsx file in your repo
      • You do not need to make any changes
      • You can upgrade to isbot@5 independent of the React Router v7 upgrade
    • If you have isbot@3 in your package.json and you do not have your own entry.server.tsx file in your repo
      • You are using the internal default entry provided by React Router v7 and you will need to upgrade to isbot@5 in your package.json
  • @react-router/dev - For Remix consumers migrating to React Router, Vite manifests (i.e. .vite/manifest.json) are now written within each build subdirectory, e.g. build/client/.vite/manifest.json and build/server/.vite/manifest.json instead of build/.vite/client-manifest.json and build/.vite/server-manifest.json. This means that the build output is now much closer to what you'd expect from a typical Vite project. (#11573)
    • Originally the Remix Vite plugin moved all Vite manifests to a root-level build/.vite directory to avoid accidentally serving them in production, particularly from the client build. This was later improved with additional logic that deleted these Vite manifest files at the end of the build process unless Vite's build.manifest had been enabled within the app's Vite config. This greatly reduced the risk of accidentally serving the Vite manifests in production since they're only present when explicitly asked for. As a result, we can now assume that consumers will know that they need to manage these additional files themselves, and React Router can safely generate a more standard Vite build output.

Minor Changes

  • react-router - Params, loader data, and action data as props for route component exports (#11961)
  • react-router - Add route module type generation (#12019)
  • react-router - Remove duplicate RouterProvider implementations (#11679)
  • react-router - Stabilize unstable_dataStrategy (#11969)
  • react-router - Stabilize unstable_patchRoutesOnNavigation (#11970)
  • react-router - Add prefetching support to Link/NavLink when using Remix SSR (#11402)
  • react-router - Enhance ScrollRestoration so it can restore properly on an SSR'd document load (#11401)
  • @react-router/dev - Add support for the prerender config in the React Router vite plugin, to support existing SSG use-cases (#11539)
  • @react-router/dev - Remove internal entry.server.spa.tsx implementation which was not compatible with the Single Fetch async hydration approach (#11681)
  • @react-router/serve: Update express.static configurations to support new prerender API (#11547)
    • Assets in the build/client/assets folder are served as before, with a 1-year immutable Cache-Control header
    • Static files outside of assets, such as pre-rendered .html and .data files are not served with a specific Cache-Control header
    • .data files are served with Content-Type: text/x-turbo
      • For some reason, when adding this via express.static, it seems to also add a Cache-Control: public, max-age=0 to .data files

Patch Changes

  • Replace substr with substring (#12080)
  • react-router - Fix redirects returned from loaders/actions using data() (#12021)
  • @react-router/dev - Enable prerendering for resource routes (#12200)
  • @react-router/dev - resolve config directory relative to flat output file structure (#12187)

Changes by Package

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 Dec 10, 2024
@depfu depfu bot requested review from canova and julienw December 10, 2024 03:11
Copy link

netlify bot commented Dec 10, 2024

Deploy Preview for firefox-devtools-react-contextmenu failed.

Name Link
🔨 Latest commit 7d4d576
🔍 Latest deploy log https://app.netlify.com/sites/firefox-devtools-react-contextmenu/deploys/6757b1624a808d00085428fb

@julienw
Copy link

julienw commented Dec 10, 2024

We're not ready to update to node v20 for now, we'd like to update all the projects at once. Probably some time in January.
Closing for now.

@julienw julienw closed this Dec 10, 2024
@depfu depfu bot deleted the depfu/update/yarn/react-router-dom-7.0.2 branch December 10, 2024 10:59
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