Releases: remix-run/react-router
v6.14.1
Patch Changes
- Fix loop in
unstable_useBlocker
when used with an unstable blocker function (#10652) - Fix issues with reused blockers on subsequent navigations (#10656)
- Updated dependencies:
@remix-run/[email protected]
Full Changelog: https://github.com/remix-run/react-router/compare/[email protected]@6.14.1
v6.14.0
What's Changed
JSON/Text Submissions
6.14.0
adds support for JSON and Text submissions via useSubmit
/fetcher.submit
since it's not always convenient to have to serialize into FormData
if you're working in a client-side SPA. To opt-into these encodings you just need to specify the proper formEncType
:
Opt-into application/json
encoding:
function Component() {
let navigation = useNavigation();
let submit = useSubmit();
submit({ key: "value" }, { method: "post", encType: "application/json" });
// navigation.formEncType => "application/json"
// navigation.json => { key: "value" }
}
async function action({ request }) {
// request.headers.get("Content-Type") => "application/json"
// await request.json() => { key: "value" }
}
Opt-into text/plain
encoding:
function Component() {
let navigation = useNavigation();
let submit = useSubmit();
submit("Text submission", { method: "post", encType: "text/plain" });
// navigation.formEncType => "text/plain"
// navigation.text => "Text submission"
}
async function action({ request }) {
// request.headers.get("Content-Type") => "text/plain"
// await request.text() => "Text submission"
}
Please note that to avoid a breaking change, the default behavior will still encode a simple key/value JSON object into a FormData
instance:
function Component() {
let navigation = useNavigation();
let submit = useSubmit();
submit({ key: "value" }, { method: "post" });
// navigation.formEncType => "application/x-www-form-urlencoded"
// navigation.formData => FormData instance
}
async function action({ request }) {
// request.headers.get("Content-Type") => "application/x-www-form-urlencoded"
// await request.formData() => FormData instance
}
This behavior will likely change in v7 so it's best to make any JSON object submissions explicit with formEncType: "application/x-www-form-urlencoded"
or formEncType: "application/json"
to ease your eventual v7 migration path.
Minor Changes
- Add support for
application/json
andtext/plain
encodings foruseSubmit
/fetcher.submit
. To reflect these additional types,useNavigation
/useFetcher
now also containnavigation.json
/navigation.text
andfetcher.json
/fetcher.text
which include the json/text submission if applicable. (#10413)
Patch Changes
- When submitting a form from a
submitter
element, prefer the built-innew FormData(form, submitter)
instead of the previous manual approach in modern browsers (those that support the newsubmitter
parameter) (#9865)- For browsers that don't support it, we continue to just append the submit button's entry to the end, and we also add rudimentary support for
type="image"
buttons - If developers want full spec-compliant support for legacy browsers, they can use the
formdata-submitter-polyfill
- For browsers that don't support it, we continue to just append the submit button's entry to the end, and we also add rudimentary support for
- Call
window.history.pushState/replaceState
before updating React Router state (instead of after) so thatwindow.location
matchesuseLocation
during synchronous React 17 rendering (#10448)β οΈ Note: generally apps should not be relying onwindow.location
and should always referenceuseLocation
when possible, aswindow.location
will not be in sync 100% of the time (due topopstate
events, concurrent mode, etc.)
- Avoid calling
shouldRevalidate
for fetchers that have not yet completed a data load (#10623) - Strip
basename
from thelocation
provided to<ScrollRestoration getKey>
to match theuseLocation
behavior (#10550) - Strip
basename
from locations provided tounstable_useBlocker
functions to match theuseLocation
behavior (#10573) - Fix
unstable_useBlocker
key issues inStrictMode
(#10573) - Fix
generatePath
when passed a numeric0
value parameter (#10612) - Fix
tsc --skipLibCheck:false
issues on React 17 (#10622) - Upgrade
typescript
to 5.1 (#10581)
Full Changelog: https://github.com/remix-run/react-router/compare/[email protected]@6.14.0
v6.13.0
What's Changed
6.13.0
is really a patch release in spirit but comes with a SemVer minor bump since we added a new future flag.
The tl;dr; is that 6.13.0
is the same as 6.12.0
bue we've moved the usage of React.startTransition
behind an opt-in future.v7_startTransition
future flag because we found that there are applications in the wild that are currently using Suspense
in ways that are incompatible with React.startTransition
.
Therefore, in 6.13.0
the default behavior will no longer leverage React.startTransition
:
<BrowserRouter>
<Routes>{/*...*/}</Routes>
</BrowserRouter>
<RouterProvider router={router} />
If you wish to enable React.startTransition
, pass the future flag to your router component:
<BrowserRouter future={{ v7_startTransition: true }}>
<Routes>{/*...*/}</Routes>
</BrowserRouter>
<RouterProvider router={router} future={{ v7_startTransition: true }}/>
We recommend folks adopt this flag sooner rather than later to be better compatible with React concurrent mode, but if you run into issues you can continue without the use of React.startTransition
until v7. Issues usually boil down to creating net-new promises during the render cycle, so if you run into issues when opting into React.startTransition
, you should either lift your promise creation out of the render cycle or put it behind a useMemo
.
Minor Changes
- Move
React.startTransition
usage behinds a future flag (#10596)
Patch Changes
- Work around webpack/terser
React.startTransition
minification bug in production mode (#10588)
Full Changelog: https://github.com/remix-run/react-router/compare/[email protected]@6.13.0
v6.12.1
Warning
Please use version6.13.0
or later instead of6.12.0
/6.12.1
. These versions suffered from some Webpack build/minification issues resulting failed builds or invalid minified code in your production bundles. See #10569 and #10579 for more details.
Patch Changes
- Adjust feature detection of
React.startTransition
to fix webpack + react 17 compilation error (#10569)
Full Changelog: https://github.com/remix-run/react-router/compare/[email protected]@6.12.1
v6.12.0
Warning
Please use version6.13.0
or later instead of6.12.0
/6.12.1
. These versions suffered from some Webpack build/minification issues resulting failed builds or invalid minified code in your production bundles. See #10569 and #10579 for more details.
What's Changed
With 6.12.0
we've added better support for suspending components by wrapping the internal router state updates in React.startTransition
. This means that, for example, if one of your components in a destination route suspends and you have not provided a Suspense
boundary to show a fallback, React will delay the rendering of the new UI and show the old UI until that asynchronous operation resolves. This could be useful for waiting for things such as waiting for images or CSS files to load (and technically, yes, you could use it for data loading but we'd still recommend using loaders for that π). For a quick overview of this usage, check out Ryan's demo on Twitter.
Minor Changes
- Wrap internal router state updates with
React.startTransition
(#10438)
Patch Changes
- Allow fetcher revalidations to complete if submitting fetcher is deleted (#10535)
- Re-throw
DOMException
(DataCloneError
) when attempting to perform aPUSH
navigation with non-serializable state. (#10427) - Ensure revalidations happen when hash is present (#10516)
- Upgrade
jest
andjsdom
(#10453) - Updated dependencies:
@remix-run/[email protected]
(Changelog)
Full Changelog: https://github.com/remix-run/react-router/compare/[email protected]@6.12.0
v6.11.2
Patch Changes
- Fix
basename
duplication in descendant<Routes>
inside a<RouterProvider>
(#10492) - Fix bug where initial data load would not kick off when hash is present (#10493)
- Export
SetURLSearchParams
type (#10444) - Fix Remix HMR-driven error boundaries by properly reconstructing new routes and
manifest
in_internalSetRoutes
(#10437)
Full Changelog: https://github.com/remix-run/react-router/compare/[email protected]@6.11.2
v6.11.1
Patch Changes
- Fix usage of
Component
API within descendant<Routes>
(#10434) - Fix bug when calling
useNavigate
from<Routes>
inside a<RouterProvider>
(#10432) - Fix usage of
<Navigate>
in strict mode when using a data router (#10435) - Fix
basename
handling when navigating without a path (#10433) - "Same hash" navigations no longer re-run loaders to match browser behavior (i.e.
/path#hash -> /path#hash
) (#10408)
Full Changelog: https://github.com/remix-run/react-router/compare/[email protected]@6.11.1
v6.11.0
What's Changed
Minor Changes
- Enable
basename
support inuseFetcher
(#10336)- If you were previously working around this issue by manually prepending the
basename
then you will need to remove the manually prependedbasename
from yourfetcher
calls (fetcher.load('/basename/route') -> fetcher.load('/route')
)
- If you were previously working around this issue by manually prepending the
- Updated dependencies:
@remix-run/[email protected]
(Changelog)
Patch Changes
- When using a
RouterProvider
,useNavigate
/useSubmit
/fetcher.submit
are now stable across location changes, since we can handle relative routing via the@remix-run/router
instance and get rid of our dependence onuseLocation()
(#10336)- When using
BrowserRouter
, these hooks remain unstable across location changes because they still rely onuseLocation()
- When using
- Fetchers should no longer revalidate on search params changes or routing to the same URL, and will only revalidate on
action
submissions orrouter.revalidate
calls (#10344) - Fix inadvertent re-renders when using
Component
instead ofelement
on a route definition (#10287) - Fail gracefully on
<Link to="//">
and other invalid URL values (#10367) - Switched from
useSyncExternalStore
touseState
for internal@remix-run/router
router state syncing in<RouterProvider>
. We found some subtle bugs where router state updates got propagated before other normaluseState
updates, which could lead to foot guns inuseEffect
calls. (#10377, #10409) - Log loader/action errors caught by the default error boundary to the console in dev for easier stack trace evaluation (#10286)
- Fix bug preventing rendering of descendant
<Routes>
whenRouterProvider
errors existed (#10374) - Fix detection of
useNavigate
in the render cycle by setting theactiveRef
in a layout effect, allowing thenavigate
function to be passed to child components and called in auseEffect
there (#10394) - Allow
useRevalidator()
to resolve a loader-driven error boundary scenario (#10369) - Enhance
LoaderFunction
/ActionFunction
return type to preventundefined
from being a valid return value (#10267) - Ensure proper 404 error on
fetcher.load
call to a route without aloader
(#10345) - Decouple
AbortController
usage between revalidating fetchers and the thing that triggered them such that the unmount/deletion of a revalidating fetcher doesn't impact the ongoing triggering navigation/revalidation (#10271)
Full Changelog: https://github.com/remix-run/react-router/compare/[email protected]@6.11.0
v6.10.0
What's Changed
We recently published a post over on the Remix Blog titled "Future Proofing Your Remix App" that goes through our strategy to ensure smooth upgrades for your Remix and React Router apps going forward. React Router 6.10.0
adds support for these flags (for data routers) which you can specify when you create your router:
const router = createBrowserRouter(routes, {
future: {
// specify future flags here
},
});
You can also check out the docs here and here.
Minor Changes
-
The first future flag being introduced is
future.v7_normalizeFormMethod
which will normalize the exposeduseNavigation()/useFetcher()
formMethod
fields as uppercase HTTP methods to align with thefetch()
(and some Remix) behavior. (#10207)- When
future.v7_normalizeFormMethod
is unspecified or set tofalse
(default v6 behavior),useNavigation().formMethod
is lowercaseuseFetcher().formMethod
is lowercase
- When
future.v7_normalizeFormMethod === true
:useNavigation().formMethod
is UPPERCASEuseFetcher().formMethod
is UPPERCASE
- When
Patch Changes
- Fix
createStaticHandler
to also check forErrorBoundary
on routes in addition toerrorElement
(#10190) - Fix route ID generation when using Fragments in
createRoutesFromElements
(#10193) - Provide fetcher submission to
shouldRevalidate
if the fetcher action redirects (#10208) - Properly handle
lazy()
errors during router initialization (#10201) - Remove
instanceof
check forDeferredData
to be resilient to ESM/CJS boundaries in SSR bundling scenarios (#10247) - Update to latest
@remix-run/[email protected]
(#10216)
Full Changelog: https://github.com/remix-run/react-router/compare/[email protected]@6.10.0
v6.9.0
What's Changed
Minor Changes
-
React Router now supports an alternative way to define your route
element
anderrorElement
fields as React Components instead of React Elements. You can instead pass a React Component to the newComponent
andErrorBoundary
fields if you choose. There is no functional difference between the two, so use whichever approach you prefer π. You shouldn't be defining both, but if you doComponent
/ErrorBoundary
will "win". (#10045)Example JSON Syntax
// Both of these work the same: const elementRoutes = [{ path: '/', element: <Home />, errorElement: <HomeError />, }] const componentRoutes = [{ path: '/', Component: Home, ErrorBoundary: HomeError, }] function Home() { ... } function HomeError() { ... }
Example JSX Syntax
// Both of these work the same: const elementRoutes = createRoutesFromElements( <Route path='/' element={<Home />} errorElement={<HomeError /> } /> ); const componentRoutes = createRoutesFromElements( <Route path='/' Component={Home} ErrorBoundary={HomeError} /> ); function Home() { ... } function HomeError() { ... }
-
Introducing Lazy Route Modules! (#10045)
In order to keep your application bundles small and support code-splitting of your routes, we've introduced a new
lazy()
route property. This is an async function that resolves the non-route-matching portions of your route definition (loader
,action
,element
/Component
,errorElement
/ErrorBoundary
,shouldRevalidate
,handle
).Lazy routes are resolved on initial load and during the
loading
orsubmitting
phase of a navigation or fetcher call. You cannot lazily define route-matching properties (path
,index
,children
) since we only execute your lazy route functions after we've matched known routes.Your
lazy
functions will typically return the result of a dynamic import.// In this example, we assume most folks land on the homepage so we include that // in our critical-path bundle, but then we lazily load modules for /a and /b so // they don't load until the user navigates to those routes let routes = createRoutesFromElements( <Route path="/" element={<Layout />}> <Route index element={<Home />} /> <Route path="a" lazy={() => import("./a")} /> <Route path="b" lazy={() => import("./b")} /> </Route> );
Then in your lazy route modules, export the properties you want defined for the route:
export async function loader({ request }) { let data = await fetchData(request); return json(data); } // Export a `Component` directly instead of needing to create a React Element from it export function Component() { let data = useLoaderData(); return ( <> <h1>You made it!</h1> <p>{data}</p> </> ); } // Export an `ErrorBoundary` directly instead of needing to create a React Element from it export function ErrorBoundary() { let error = useRouteError(); return isRouteErrorResponse(error) ? ( <h1> {error.status} {error.statusText} </h1> ) : ( <h1>{error.message || error}</h1> ); }
An example of this in action can be found in the
examples/lazy-loading-router-provider
directory of the repository. For more info, check out thelazy
docs.π Huge thanks to @rossipedia for the Initial Proposal and POC Implementation.
Patch Changes
- Improve memoization for context providers to avoid unnecessary re-renders (#9983)
- Fix
generatePath
incorrectly applying parameters in some cases (#10078) [react-router-dom-v5-compat]
Add missed data router API re-exports (#10171)
Full Changelog: https://github.com/remix-run/react-router/compare/[email protected]@6.9.0