Before we get started, just in case you missed them, here are quick links to the announcement post and Changelog:
First run the upgrade command, then we’ll go through all the breaking changes one by one:
yarn rw upgrade
Heads up
If you don’t have a clean working directory, commit or stash your changes before running codemods as they can overwrite files!
Breaking changes
- React 18
- RedwoodJS packages now target Node.js v18.16
- TypeScript v5
- In Cells, GraphQL-client related props are now in an object named
queryResult
- Cells check both query fields for data
validateWith
is nowvalidateWithSync
- Chakra UI v2
- More types for auth methods
- Auth0 v2
- Supabase v2
- SimpleWebAuthn v7
- Deprecating support for the Serverless Framework
Precautions
- Different
jest.restoreAllMocks()
behavior - Small
@apollo/client
type change - MSW (Mock Service Worker) v1
- dbAuth security improvements to
resetToken
- The logger now prints metadata locally
React 18 Changes
React 18
Codemod Available
To implement this step via automated codemod, run:
npx @redwoodjs/codemods@canary upgrade-to-react-18
Most of the breaking changes introduced by React 18 were internal to the framework, but you may encounter behavioral changes that are breaking in your pages and components. For example, something that rendered twice before now may render only once. Or, a component or library you’re using doesn’t support React 18 yet. If you encounter anything, please share it with us here! At the end of this upgrade guide is a running list of some of the changes users have encountered: Redwood v5.0.0 Upgrade Guide.
As far as upgrading, there’s only two guaranteed changes you’ll have to make. If you have a custom web index, there’s a third change. But regardless of your setup, the codemod should take care of these changes for you:
- Change the versions of React in your web side’s package.json to
v18.2.0
:
// web/package.json
dependencies: {
- "react": "18.2.0",
+ "react": "18.2.0",
- "react-dom": "18.2.0"
+ "react-dom": "18.2.0"
...
- Update your project’s
web/src/index.html
file by removing theprerenderPlaceholder
template syntax:
<!-- web/src/index.html -->
...
<body>
<div id="redwood-app">
- <!-- Please keep the line below for prerender support. -->
- <%= prerenderPlaceholder %>
</div>
</body>
</html>
If you added other HTML elements to the react root, (<div id="redwood-app"></div>
) you’ll need to move them elsewhere (probably into a layout), or else you’ll encounter hydration errors while prerendering. React expects to have full control of this DOM node.
- If you’ve used the
yarn rw setup custom-web-index
command to set up your own web entry point, or use Vite, you’ll have to make the changes to how React 18 wants to mount to the DOM yourself, but you can refer to the new default template here: https://github.com/redwoodjs/redwood/blob/710c316c4272222293c49001881024774b8b9fcb/packages/web/src/entry/index.js. Just replaceimport App from '~redwood-app-root'
withimport App from './App'
and you should be good to go.
Besides upgrading React, we’ve also upgraded its TypeScript types and React Testing Library. For TypeScript and React Testing Library users (that is, you’re writing your tests in TypeScript), there’s a small-but-potentially-annoying type change to the generics in the renderHook
function. If you’re consuming its return type (e.g. doing something like ReturnType<typeof renderHook>)
, see the change we had to make here: feat(react): Upgrade to React 18 by virtuoushub · Pull Request #4992 · redwoodjs/redwood · GitHub
Most of React’s upgrade guide won’t apply to you, but there are certain sections that are worth reading, like Automatic Batching and Other Breaking Changes. It’s also worth skimming their new docs.
PRs:
- https://github.com/redwoodjs/redwood/pull/4992
- Keep prerender component tree the same as regular render tree by Tobbe · Pull Request #7760 · redwoodjs/redwood · GitHub
- Don't error out when using Vite with RW v5 by Tobbe · Pull Request #8040 · redwoodjs/redwood · GitHub
RedwoodJS packages now target Node.js v18.16
Codemod Available
To implement this step via automated codemod, run:
npx @redwoodjs/codemods@canary update-node-engine-to-18
All Redwood’s deploy targets now support deploying with Node.js 18 LTS, so we’ve bumped the version of Node.js that Redwood packages target to v18.16. The codemod above updates the node version in the engines field in the root package.json.
After upgrading your project to v5, please update the version of Node.js your deploy provider uses to 18. Refer to your deploy provider’s relevant documentation. It varies from selecting a different version from a dropdown in a UI to changing the value in an .nvmrc
file.
PR: Bump the Node.js build target to 18 LTS by jtoar · Pull Request #8106 · redwoodjs/redwood · GitHub
TypeScript v5
TypeScript v5 was released in March and we were able to include it in this major because there was surprisingly little we had to do to upgrade to it. We were actually able to remove some of the @ts-expect-error
directives we had because they were no longer errors.
We don’t have any explicit changes for you to make. It’ll vary based on your use of TypeScript. Have a look at the announcement here and consider if any of the breaking changes affects you. If you use enums
, pay particular attention to the new way they behave:
In Cells, GraphQL-client related props are now in an object named queryResult
Codemod Available
To implement this step via automated codemod, run:
npx @redwoodjs/codemods@canary cell-query-result
The components in a Cell (Failure
, Loading
, Empty
, and Success
) have access to a lot of props. They have access to basically everything your GraphQL client’s useQuery
hook returns. In Apollo Client’s case, it’s a lot. So there’s a non-zero chance that one of its props overwrites one of your data props. By far the most common case is if you have a model named “client” it’ll be overwritten by Apollo Client’s “client” prop (which gives you direct access to the GraphQL client).
This has come up often enough that we’ve decided to stop spreading the GraphQL-client related props that useQuery
returns. Instead, they’re all accessible via an object named queryResult
.
This one was a bit trickier for us to codemod, and while we’ve added a lot of tests, do let us know if the codemod doesn’t work for you!
Cells check both query fields for data
Codemod Available
To implement this step via automated codemod, run:
npx @redwoodjs/codemods@canary detect-empty-cells
Note that this codemod doesn’t actually change your code, but warns you about Cells who’s behavior may have changed.
Cells with more than one field on the GraphQL query type behave somewhat counterintuitively:
export const QUERY = gql`
users {
name
}
posts {
title
}
`
If users
doesn’t return any data, but posts
does, the Cell shows Empty
instead of Success
. This probably isn’t what you expect, so we’ve fixed it in this release to show Success
if there’s any data, not only if there’s data in the first field on the query type.
The codemod above scans for Cells in your project that are affected by this change, and alerts you to them. If the new behavior isn’t what you want, consider exporting your own isEmpty
function from the Cell that only checks the first field.
validateWith
is now validateWithSync
Codemod Available
To implement this step via automated codemod, run:
npx @redwoodjs/codemods@canary rename-validate-with
We’ve decided to make the validateWith
function from @redwoodjs/api
take an asynchronous function instead of a synchronous one. Following Node’s convention, we’ve added a synchronous version of validateWith
, validateWithSync
. This means that all calls to validateWith
need to be changed to validateWithSync
. While you could do a search and replace, The codemod should take care of that for you.
Chakra UI v2
Now that Redwood is on React 18, you can upgrade to Chakra UI v2. The upgrade process will depend on your use of Chakra, but at the very least you’ll have to update the @chakra-ui/react
and framer-motion
packages in your web side’s package.json:
// web/package.json
dependencies: {
- '@chakra-ui/react@^1',
+ '@chakra-ui/react@^2',
- 'framer-motion@^8',
+ 'framer-motion@^9',
...
See Chakra’s upgrade guide for more:
More types for auth methods
We’ve continued improving the types for all of Redwood’s auth integrations. While there’s nothing breaking here in terms of behavior, the TypeScript changes we’ve made for correctness may introduce type errors, which may fail your build.
Auth0 v2
Codemod Available
To implement this step via automated codemod, run:
npx @redwoodjs/codemods@canary update-auth0-to-v2
Auth0 has released v2 of their SPA SDKs. There’s a simple breaking change to the way the SDK is instantiated in web/src/auth.tsx
that we’ve provided a codemod for, but there’s more behavioral breaking changes that may affect you depending on your use of their SDK. Refer to their migration guide: auth0-spa-js/MIGRATION_GUIDE.md at main · auth0/auth0-spa-js · GitHub.
Supabase v2
Supabase released v2 of their JS SDK some time ago—we’ve finally upgraded to it in this release! The key changes are improved TypeScript support and new authentication methods. We’ll walk through the breaking changes here, but for all the details (features, fixes, etc.), check out these links:
The major breaking change in v2 is that Supabase removed their SDK’s catch-all signIn()
method in favor of more-explicit method signatures like signInWithPassword()
and signInWithOtp()
. So the logIn
method from Redwood’s useAuth
hook now needs to know which authentication strategy you want to use via a new prop, authMethod
. Here’s an example:
// In a React component...
const { logIn } = useAuth()
// Log in using an email and a password
await logIn({
authMethod: 'password',
email: 'example@email.com',
password: 'example-password',
})
// Or, log in using a one time password (OTP)
await logIn({
authMethod: 'otp',
email: 'example@email.com',
options: {
emailRedirectTo: 'https://example.com/welcome'
}
})
Lastly, some client-specific methods can no longer be accessed from useAuth()
directly, such as verifyOtp
, onAuthStateChange
, and getSession
. To use these methods, you’ll have to access the client
directly. Here’s an example:
// In a React component...
const { client } = useAuth()
useEffect(() => {
const { data, error } = await client.getSession()
}, [client])
Check out the new RedwoodJS docs for more examples of how to use the new SDK:
PR: feature: Upgrade Supabase Auth to v2 by dthyresson · Pull Request #7719 · redwoodjs/redwood · GitHub
SimpleWebAuthn v7
We upgraded the SimpleWebAuthn packages from v6 to v7. The upgrade was fairly simple; it mainly involved adding Buffer.from
to a few places in the code to change SimpleWebAuthn’s new platform-agnostic API back to a Node.js one.
To upgrade, simply update the versions of the @simplewebauthn/*
packages in your package.jsons:
// api package.json
dependencies: {
- "@simplewebauthn/server": "^6"
+ "@simplewebauthn/server": "^7"
...
// web package.json
dependencies: {
- "@simplewebauthn/browser": "^6",
+ "@simplewebauthn/browser": "^7",
...
You can refer to the full release notes here:
PR: Upgrade simplewebauthn packages by jtoar · Pull Request #7477 · redwoodjs/redwood · GitHub
Deprecating support for the Serverless Framework
We’re deprecating support for the Serverless Framework. See this topic for details:
Different jest.restoreAllMocks()
behavior
Precaution
This most likely isn’t a breaking change for you; we’re just listing it as a precaution. It’s also something you may have already encountered in v4.5.0 due to upstream dependencies.
Jest v29.4.3 fixed a bug in jest.restoreAllMocks()
, but we had to change our code to accommodate upgrading to this version. That means you could too. If you have tests that use jest.restoreAllMocks()
and they’re failing after upgrading to v5, consider changing jest.restoreAllMocks()
to jest.resetAllMocks()
like we did here:
Small @apollo/client
type change
Precaution
This most likely isn’t a breaking change for you; we’re just listing it as a precaution.
Apollo Client made a TypeScript change for the better, but because the change uses the extends
keyword to enforce more restrictions on a generic, there’s a chance it’ll break type checking in your project. It’s fairly unlikely that it will because in practice, you couldn’t pass anything you wanted to the useQuery hook, but for good measure, here’s the change we had to make to the framework, and the new OperationVariables
type (it’s just a Record
):
- fix(deps): update dependency @apollo/client to v3.7.10 by renovate[bot] · Pull Request #7520 · redwoodjs/redwood · GitHub
- apollo-client/src/core/types.ts at 4e187928b59c8f3bb51d66fa3257785d81561c11 · apollographql/apollo-client · GitHub
MSW (Mock Service Worker) v1
Precaution
This most likely isn’t a breaking change for you; we’re just listing it as a precaution.
The breaking change was internal to @redwoodjs/testing
, and it only broke type checking. But we figured there’s a chance you’re using MSW more directly in your project. If you are, have a look at the type change in v1 here. It’s a straightforward rename:
dbAuth security improvements to resetToken
Precaution
This most likely isn’t a breaking change for you; we’re just listing it as a precaution.
v5 brings a major security improvement to dbAuth: it no longer stores raw resetToken
s in the database and instead stores the sha256 hash of the token. This makes it so that if a database is compromised, an attacker can’t steal resetToken
s and reset users’ passwords.
We’re listing this as a precaution for two reasons:
- If you refetch the
User
object in the dbauth handler, this may be a breaking change for you because this new functionality relies on mutating theUser
object. We believe that it’s rather unlikely that you’re doing this, but if you happen to be, reach out to us here or on GitHub and we’ll see if we can design a better pattern. - If you upgrade to v5 while password resets are in-transit, they could be in limbo due to the difference in the way they’re handled between v4 and v5. For low-to-medium traffic sites, this probably isn’t a problem. (Maybe just have an error message that asks users to try again if the token doesn’t match.)
For high-traffic sites, or for the best end-user experience, you can temporarily disable password resets while upgrading. Just query your database and wait until no more valid reset tokens exist (by looking atresetTokenExpiresAt
), then upgrade and re-enable password resets.
The logger now prints metadata locally
Precaution
This most likely isn’t a breaking change for you; we’re just listing it as a precaution.
In v4 and before, the logger behaved somewhat differently between development and production. In development, if you logged metadata, like logger.debug({ user }, 'my user')
(where the first argument, { user }
is the metadata), only the second argument and on, “my user” here, would be printed to the terminal. Now the full user object will be printed too, making it more inline with what happens in production.
React 18 Changes
Here’s a running list of some gotchas users have encountered with React 18.
@apollo/client
useMutation result
The useMutation
hook from @apollo/client
returns a tuple that has a mutate function and an object representing the mutation result:
const [mutate, mutationResult] = useMutation(MUTATION)
In version v4.x of Redwood, you could depend on mutationResult
to be updated in useMutation
’s onCompleted
callback:
const [mutate, mutationResult] = useMutation(MUTATION, {
onCompleted: () => {
if (mutationResult.data.myDataProperty === null) {
// do something
}
},
})
But now, in v5 with React 18, mutationResult
doesn’t seem to update in this callback. Instead, opt for the data
prop from onCompleted
:
const [mutate, mutationResult] = useMutation(MUTATION, {
onCompleted: (data) => {
if (data.myDataProperty === null) {
// do something
}
},
})
Thanks to @razzeee for reporting this!