What's Coming in React 19 Beta
Kent C. Dodds walks through the React 19 Beta announcement blog post, providing a running commentary on the new features and what they mean for your applications going forward.
Topics include:
- Actions
- The
use
Hook - React Server Components & Actions
- Changes to
ref
- New Hooks
- Improved style sheet & script tag support
Transcript
00:00 What's up, Epic web devs? I'm excited to talk with you about React 19 beta. This was released on April 25th, and we are excited to kind of explore what we have to look forward to in React 19. Before we get into it, I just want to call out something that I think is really awesome about this, and that is how few breaking changes there are. React does such a remarkably good job of just progressing without regressing in a way that, like, yes, there will be breaking changes and there are some, not just in the APIs, but in the way that we build applications, but React allows us to come along with it over the years.
00:44 And so components that you wrote forever ago may be using some deprecated APIs, but there's always either a code mod or some external package or something that you can use. And I just, I love that about React. So with all that said, Let's get into React 19 beta. So, first of all, React wants to make sure that you know that this is a beta release. They don't want you shipping production stuff, but libraries and frameworks can start building on top of React 19 and experimenting with things.
01:18 It is published to NPM and I have used it and it works quite well. So this is good. So let's start with what's new. Actions. So Finally, React has realized that a big half of building web applications involves making data mutations and so we're going to be getting some data mutation stuff.
01:41 So here's what we had to do before Actions. We have used states all over the place. We're managing our pending state. We've got a whole bunch of race condition problems and resubmissions and like what do you do with all this impossible states, all sorts of problems with this code. But like this is very common code and heaven forbid you throw a use effect in here as well.
02:06 And so yeah, there's a lot of problems with this, and this is why I use Remix, because it drastically simplifies a lot of this stuff. That said, There are some asynchronous things that we're doing that don't involve a network, they're not making a fetch request, it's not really a form action submission. And so having a more primitive thing would be really nice to handle these types of things. I'm thinking like web API stuff where you're talking to the webcam or to a Bluetooth device or something like that. So some asynchrony in handling that stuff would be quite nice.
02:39 And that is what we get with this, well, half of what we get is with useTransition. So we're adding support for async functions in transitions to handle pending states, errors, and form, and Optimistic updates automatically. Magic. Always gotta worry when you see the word automatically, but I think this is gonna be good. So, or at least most of it will.
03:01 So a used transition will handle this. We have our useTransition and we get our isPending from that and then we do this within a transition. This is an async function. So we've had useTransition for a while in React 18 for a couple of years now, But now you're able to do an async transition, so you can do all your async stuff and the pending states will work and React will keep the old stuff up while the new stuff is happening and trying to update and everything works nicely. And in Epic React V2, we actually use this.
03:35 So pretty cool. So these functions that use async transitions are called actions, and they automatically manage submitting data for you. They handle the pending state. Optimistic updates will come from the use optimistic hook, which we will talk about here in a little bit. Error handling is managed via error boundaries.
03:55 So bringing us some really nice declarative error handling, just like we get with, well, you'd get with suspense boundaries, but here in this case, we get our pending state with is pending. And then we have form support. We'll talk about form actions here in a little bit. So as a part of this, the optimistic side of it is use optimistic. And then we also have use action state and use form status that we'll take a look at.
04:26 So this is kind of like take that previous example and slurp it up all into a couple of hooks that are specific to this type of use case because it's pretty common. So here we have useActionState. Here's our async function and our initial state. I think that's what the initial state, actually useActionState. We'll have to look at the useActionState API because that second argument, I am not positive what that second argument is.
04:54 Oh, hold on. We're still in the blog post right here. Yeah, I'll have to look into what that second argument is all about. I've forgotten. But in any case, this first argument is our action function.
05:11 So here's the thing. I'm not a super fan of this right here. So what they want this to do is resemble a reducer. A lot of people have gotten used to reducers. So here's the previous value.
05:24 This is the previous thing you returned. And here's the form data. So this would be like your action. So the submission. So here we're passing that, sorry, let me back that up.
05:35 So we've got our submit action that we get from useActionState, when you call that, it calls this function, you pass that submit action to the form action, and React will handle preventing the default behavior, so no full page refresh, and it will call your action function here. But it calls it with the previous state, so it calls it with the previous value you passed here, and that's why I'm thinking this is the initial state, that makes sense, it also kind of resembles the reducer API as well. But yeah, so if you are calling this form action multiple times, you'll get the previous thing that was returned, whether that be, I suppose, the error or the return value here. I do not think that is useful at all. And so I'm not a super fan of this, but we shall see.
06:21 The form data that itself is quite nice. I like that the React team is leaning more heavily on the primitives of the web and form data being one of those primitives, I love that. It does come with the drawback that there's limited type safety here. So the type that you get when you call formdata.get is gonna be a form entry or a field entry or null. And a field entry is either a string or a file.
06:52 And there's like no association here. And there couldn't be, right? Because this could be put right here and then maybe you're rendering a custom component and under that we're rendering the name or something like that. So the TypeScript type system just isn't smart enough to be able to resolve what that form data could possibly be. And so there are libraries for this And I would expect that Conform, my personal favorite form library for React and Remix is gonna adopt some of this, or at least make some of this a lot easier to consume in a type safe way.
07:30 So a big fan of Conform, I expect that we'll have something to make this a little bit more type safe. But the point is that, yeah, we now have our error handling and is pending right here. We can handle that all locally. So you get the declarative API if you want to go with the error boundary and an async use transition. But if you want to go more local, then use action state can be quite handy here.
07:57 And you just pass it directly to the form. You don't have to prevent default React handles all that for you. And you can do whatever async thing in here you want. And somewhere around here, it talks about how use action state, which we're using here. And here we've got another example.
08:14 I think It talks about if there are multiple submissions, race conditions or something, React will handle that for you automatically, which is quite nice, I must say. And for those of you who've been following along at home, useFormState is the old name of this hook, but because it's not necessarily just for forms, you can have this action be useful for like a regular button click or something. They change it to use action state and then actually change the API a little bit too, which I think was an improvement. Okay, so now we've got form action. So going back to that form, passing an action function to the form, React automatically resets the form for uncontrolled components, And you can set it automatically if you like.
09:02 That's some additional information that we didn't see before. You can dive in deeper on this, but general idea or my general feeling on all of this stuff is pretty positive. I am, for my own stuff, using Remix and everything, I'm pretty much just planning on continuing to use Remix and their primitives for all of this stuff because they work really, really well. And I expect that Remix under the hood might start using some of this stuff. And especially as we get server actions, we'll talk about server actions and server components a little bit later, but as that gets support in Remix, then maybe we'll be doing a little bit more of this component-based stuff rather than the route-based stuff that we're typically doing with Remix.
09:46 So I wouldn't say I'm cautiously optimistic, I'm mostly optimistic about all of this stuff. But yeah, it is what it is, and I am looking forward to it. I think it is good. Okay, great. So useFormStatus.
10:01 This is just mostly for reusable components that you want to make that are like, this is my submit button and I have a little spinny thing when it's pending or whatever, that can be like, you want to access the form status but you want to do it somewhere else and you don't want to have to create your own context provider past the pending state and all that nonsense. This is basically a context consumer for the nearest form. So yeah, it can be done via context, but it's so common that we're gonna make our own context consumer, you know, quote unquote, and it'll grab the form status from the nearest parent form. So yeah, basically think of form as a provider and this is a context consumer. I think that's cool.
10:48 Yeah, I'm a fan of that. That makes sense. I suppose I would say the one drawback of this is it's a little bit limited in utility because it's really only useful if you're inside of the form that you're submitting. If you wanted to have some sort of global spinner at the top when they're submitting or something like that, this would not be useful for that case. And again, this is why Remix is so great because it has affordances for all of this stuff already.
11:16 And I believe that maybe in the future, Remix will adopt some of this stuff. But yeah, if you want to be a little bit more framework agnostic or something like this, this can be quite useful for a lot of cases. So it's useful. Okay, let's talk about useOptimistic. So useOptimistic is basically, let's talk about transitions for a second.
11:43 So when you want to wrap something in transition, which is what the action is doing. Remember like earlier we had the async transition that's in a start transition, all that stuff. So when you do an action, this is going to be submitted in a transition. When you change state inside of a transition, then React will go ahead and, well, if the transition's not done yet, then React is like, okay, cool, I'll remember that for when you are done and we can update that state. If once that transition is done, then React is hanging onto the old stuff and showing the user the old stuff with the pending state.
12:19 And then in the background, it's trying to render this thing and maybe it suspends something or whatever. But once everything settles, that's when it switches to the new UI. And so there's this state between when the transition starts and when everything settles, that is your pending state. So the trick is, if you want to do some optimistic UI, maybe you have a useState up here at the top that says, okay, here's my optimistic user, And then inside of your action, you say, here's my new optimistic name. Like, here we go, set optimistic name.
12:51 Let's pretend that's a useState. So if we did it with a useState like this, then that wouldn't quite work because that state transition is gonna just, or that state call update is going to be just handled by React as like, oh, okay, I'll make sure that we handle that state update when the pending state is all finished. So we basically, we like that we have a transition that kind of queues up everything and it gives us a pending state that we can render and then once things are finished then it can update to the new UI. But we want to kind of like poke out of that. So like we're inside of this box of the transition we want to poke out and say, hey, actually I do want to update the UI for this particular thing.
13:32 That's what use optimistic is all about. So we pass the default value. So here's our current name. This is, if there's no current transition going on, there's no optimistic need, it's going to fall back to whatever this value is. And then you get your optimistic name.
13:50 We render that out. And then when they make a change to their name and they submit the form, then we're gonna get the name that they submitted and we're gonna set the optimistic name. So inside of this function, we're inside that transition, we're locked, and we're showing our pending state or whatever. So we're locked in this transition, but we say, hey, set optimistic name. So we're like, poop, poop, whoop, whatever.
14:15 And we re-render so that we can show that optimistic state. So that's what's going on here. It's a bit of a funny API, and they're not showing everything about this. There's a second argument you can pass that's a function that will, here's the current state and here's your update, like go ahead and merge this so that we can show the optimistic state. That could be useful if you have like a list of items or something like that.
14:41 I have some reservations about the way that it's implemented And like, because this will force a synchronous re-render of things and yeah, there are some interesting things that go on when we're talking about use optimistic. But I would say I'm cautiously optimistic about use optimistic. I typically trust the team that they know what they're doing. But yeah, just have a couple of reservations on that. Feel free to dive into use-optimistic.
15:11 But that's how it works in general terms. Pretty cool that the React team is at least considering these types of use cases. Okay, the Use API. I'm actually a super fan of the Use hook, even though the name is kind of weird. I don't know what I would tell them to call it otherwise.
15:27 I don't really care too much. I think that what it enables is pretty cool. And so here, it's for two things. It's for context and it's for suspending on promises. So here, and actually one other thing, we have a suspense workshop in Epic React V2 that uses the use hook.
15:47 In fact, the first exercise builds the use hook. If you want to see that before I release the videos, you can find it at reactsuspense.epicweb.dev. This is the workshop. It works a lot better when you run it locally, but here, the data fetching, we, in this first exercise, we're building the use hook. And then we, in the second exercise, we actually talk about the limitation of the use hook and why you can't, well, I'll talk about here in a second, but this is the second part is why there's a limitation with the use hook.
16:21 Okay, so the way that this works is we have this comments and we have this page and the page is rendering a suspense fallback. Page is getting a comments promise because they didn't want to complicate this example by showing you how you create this promise and then how you manage caching for it and all that stuff. That's what the workshop exercise goes into. But yeah, so we have some promise that somebody created at some point, probably not in render, and then we pass that along to the comments, comments calls use, and if that promise has not resolved yet or actually even if it has, because we got like next tick of the event loop, They got to add a then handler on it to get the value all that stuff. So it's going to suspend And what that means is we're going to stop execution right here So we don't even call this next line and that's how they can make this work This could you might actually call this await if await wasn't a keyword you can make it a function because that's sort of what's going on here.
17:20 It's not an async function, but you are effectively waiting for this promise to resolve. So the way that that works under the hood is by throwing the promise. That's an implementation detail. The React team doesn't want me telling you that, but that's how it works. And so then React will catch that thrown promise.
17:39 It will attach a couple of then handlers and stuff like that, some state around that promise. And then when it resolves, and actually during that time it'll show the loading, and then when it resolves, then your component will be called again. We'll call use, it'll pass the comments promise, React will be like, oh, I've seen this promise before and oh yeah, it actually resolved. Let me go get that value and boom, I'll return that as the comments. And then we map over the comments to create whatever UI or whatever.
18:05 This is awesome, a really really fantastic way to manage asynchrony and in a declarative way. Yeah, I'm a fan of this. I think this is great. So here's the part that some there was actually a little bit of noise on X earlier about this. I don't really think this is a huge issue now that I understand how it works, but I can understand why some people might be annoyed by it.
18:30 So you cannot create a promise here. So if I, instead of comments promise, and like, maybe I don't accept these props, maybe I accept an ID or something, and then I put a fetch request in here, the problem is every time comments is called, that fetch request is gonna be called again. So we're just gonna call it, call it, call it, call it, call it. And we're making a new promise every time as well. So when we say, hey, React, I want to use this thing, React is like, oh, brand new promise.
18:52 Let's suspend. And then that promise resolves, and React's like, great, let me call comments again. And comments runs again, and there's a new fetch request, a new promise is made. And you say, oh, here's the promise, let's suspend and you just go into an infinite thing. So that's why you cannot create a promise inside of your render function or the render, yeah, we call this render function.
19:15 That's old vernacular for us class people and create class people, but this is your render function. In the body of your function component, the reason you can't create that promise because it's gonna be a new one every single time. And so instead you need to use some mechanism that's going to cache that, which is exactly what we do in the React suspense workshop. We create a caching mechanism for this. And so that when you say, hey, I wanna get comment ID six, whatever, then if it's already made that fetch request, it just returns the old promise and React's like, oh, I've seen this promise before.
19:46 I know what the value is. Here's the value. So that's what's going on here. It's really not that huge of a deal. It's not gonna be a big problem for you.
19:54 As far as like, will we use this? I expect that we'll be using a lot more of raw React stuff once Remix has time to adopt a lot of these primitives under the hood, which I think will be cool. So I do expect that in the future we'll be using the use hook potentially, maybe. Yeah, I expect so in various parts of even Remix applications, and I think that is cool. The other thing you can do with the use hook is read context.
20:24 And what's cool about use is because of the nature of use, you might need to call it conditionally. Like, let's say that somebody has some feature disabled or something. You want to conditionally say, hey, if they have this feature disabled, then don't use this promise because I don't want you to suspend. And so this allows us to conditionally suspend a component based on some other data or state or something like that. And because of that, because they made the use hook be conditional, and they made it so that the use hook can be used to consume context, you can conditionally use the use hook.
21:02 So it's breaking one of the rules of hooks, and I think that's a great thing. So here we can say, hey, if you have no children, then don't even bother consuming the context. This may seem like a premature optimization or something, like why don't you just move that to the top? What's the big deal? But it actually is a big deal because it means that heading is now no longer a consumer of the theme context and so if the theme changes, heading doesn't need to re-render.
21:26 So yeah, again, that also could be a premature optimization but I think this code is just as clear and maybe even more clear than moving this up. And so it's a nice thing to get for free by just making your code more clear. So I'm a fan of conditional context consumption and conditional promise consumption via a conditional use hook. And so yeah, I'm here for the use hook. I think it's awesome.
21:51 Okay, let's talk about server components. So this is where server components in React 19 are going to get to be a more stable thing than they are already. They're technically stable. So frameworks can consume them and frameworks make server components stable, but inside of React, behind the framework, they are unstable. That's the current status of server components.
22:15 But with React 19, I believe that it's intended that they're at least a little bit more stable, but not 100% still. So we'll see what I mean by that here in a sec. Okay, so React Server components are awesome. I've got an entire workshop on, let's see, React server components on Epic Web Dev that you can go through right now if you want to get introduced to these concepts. Build it from scratch, super cool and interesting.
22:43 But yeah, I am optimistic. I used to be cautiously optimistic about server components. Now I'm just optimistic about server components. I don't expect that I will use them until React or Remix has support for React server components. And I look forward to that future.
22:59 They're actively working on that right now. And in fact, what's really cool is they'll make them incrementally adoptable, even for people who are currently on Remix, you'll be able to incrementally adopt things. It's not going to be like, here's the old router and here's the new router, whatever. It'll just be like here's your app and you can start using server components in places, which I think is a good thing. And yeah, with server components as a concept, I think it's important to call out that they can run once at build time on your CI server or They can be run in each request using a web server.
23:33 So this is an important aspect. There's something that has been confused, or a lot of people outside of the React inner circle have been confused about. It's like, oh, server components, That must mean I now need a server, but I'm building spas and so I don't care. No, they're actually beneficial even if you're building a static site. OK, so React 19 includes all of the React server components features, including the canary channel.
23:59 This means libraries that ship with server components can now target React 19 as a peer dependency, which is awesome. OK, so the React server components and React 19 are stable, will not break between major versions. The underlying APIs used to implement React Server components, Bundler, or Framework do not follow SemVer and break between miners in React 19. This is why I'm saying they're a little bit more stable. I'm pretty confident what this means is that the React package, React and React DOM are going to be stable, but there are other packages that are specific for bundlers that have some specific APIs, like for generating the React or the JSX payload, And then for deserializing that, some of those APIs are going to potentially break between minor versions in React 19.
24:54 For those of you who are not building a framework, you probably, you shouldn't ever experience any breakages. But for those of you breaking a framework, breaking and building a framework, good luck. So that is, yeah, that is that. All right, let's talk about server actions. So server actions allow client components to call async functions executed on the server.
25:17 Now, for a client-side code that runs in the browser to call something on the server, you're not actually calling a function. Well, you technically are, but the way that it looks in your code is you look like you're importing a function from a server file, so from a file that's like accessing the database and stuff. And that might be a little concerning, but the way that the use server directive works with frameworks and bundlers is they will create a reference. So it's like an ID, could be the chunk and then the export or something like that, that allows the server to determine which action is being called by the client when that network request is being made. We actually do cover this in the React Server Components workshop that we have on Epic Web, so look forward to that.
26:07 And we actually build it, which is kind of cool. But yeah, it's kind of interesting. No directives for server components. So in React 19, in a React server components world, the default component is a server component. That's just the way that it is.
26:24 All components are server components, and it has to have useClient at the top, And then all of the components that are imported into a module that has useClient at the top, all of those components will be executed on the client. But yeah, by default, if you've got a component, that's gonna be a server component. And it's not until it's either got the directive for useClient or it's imported by another file that has that directive, will it become a client component? So that's why there's no directive for server components because that's just the default. And the other thing, I guess, while we're talking about directives is your useClient directive does not have to appear in every component that you want to have executing on the client.
27:06 Now it's just, if it's imported by a client component, then it is a client component, it will execute on the client. Think of use client as script tags, if that's helpful. So many analogies to try and help people understand server components and this new future, which is awesome because it's not breaking anything. It's just new functionality, which I think is cool. Okay, here's a relatively small breaking change that I love.
27:32 Oh, finally, the ref is a prop now. So for historical reasons, you needed to forward or use forward ref, this special function that you would, it's a higher order component actually, you would pass a component into forward ref and then it would receive a ref as a second argument. Now, why wasn't a ref always just the second argument to begin with? Well, because a legacy Context API actually passed context as the second argument. Some of you may not have known that.
28:05 Nobody uses that. It's no longer a thing that, or it's the legacy context API. So with React 19, ref is just a regular prop. You pass it, you accept it, everything's good. If you're used to using forward ref, one thing that I noticed when I was migrating some code is that whereas with forward ref you can define the shape of your ref, what type of ref it is and that works all nice and everything.
28:30 If you're typing a ref, all of the refs, this actually has something to do with the mutable refs, all refs are mutable now, nonsense. But now your ref is a ref object that is the type of the ref you want or null because the ref may not be initialized. You can't really know for sure. So as you're typing these refs, you have to add or null, which is no big deal. It's fine.
28:56 So pretty, pretty cool. If you're still using class components, then they are, it operates the way that it used to, which is don't use cost components. Okay, so this is nice. You're gonna get diffs for hydration errors. Hooray, hooray, this is so nice.
29:11 So it used to be like, yes, here's what it was on the server, here's on the client, but it didn't really give much useful information and there were like six warnings that you had to go through. Now it's all a single message and it gives a much more comprehensive diff, which would be way, way nice, so much nicer. So yeah, hooray. And that's not the last we'll hear about hydration stuff too. Here's another quick win.
29:38 Instead of rendering context provider now, you can just render the context itself. I have no idea why this wasn't the way it is from the beginning. Initially, when Context received its current API, you would use the ContextProvider, and then you'd use a ContextConsumer, which was a RenderProp API. So maybe that's why they had a Context.Provider and a Context.Consumer, and that symmetry made sense. But now that we don't use the RenderProp API anymore, we're using hooks.
30:08 It doesn't make as much sense to have this provider. So we're just going with straight up context. I'm just speculating that was the reason, but I'm pretty confident that's why. So yeah, that'll be really nice and they're going to be publishing a code mod to automatically switch you over which is nice and in the future they'll deprecate this which some of the things that they're removing have been deprecated for like five years. So who knows how long it will be.
30:35 But like, I think that's a good thing actually. It's really, really nice that React allows you to continue with the releases of React without having like, okay, hard stop. Like you get plenty of time to migrate. I just think that is awesome of them. Okay, this one is awesome, awesome.
30:52 So lots of the time when you're interacting with DOM and you're trying to initialize something or whatever, you find yourself in need of a ref inside of a use effect that handles cleanup and all of that stuff. Now, for a long time, the ref prop, you were able to pass a callback. Actually, you used to be able to pass a string. What? They're removing that finally now.
31:18 So you've always been able to pass a callback, but you couldn't pass a return or return a callback that is your cleanup function. Instead, what they would do is they would call your ref callback with null indicating, hey, it's getting removed and cleaned up and it didn't seem to always work very well. So now you return a specific cleanup function and it works awesome. Super happy with that. This means that you'll probably need fewer useRefs and you'll also likely need fewer useEffects as well.
31:52 That said, you will still probably use useRef and useEffect in some situations. In fact, in the React hooks workshop of Epic React version two. We start with the callback form and then I show you, oh, here's a situation where I actually want to use, use ref and use effect, and then we move into that. So it's cool that we can do both and I'm a fan. And yeah, works for DOM refs, refs class components using imperative handle, all cool stuff, and you no longer need to use forward ref.
32:24 Hallelujah. I was actually at the point where in Epic React V2, I was planning on just saying, you know what, let's just not call it ref. We'll just pass a regular prop. We'll call it something else entirely. So I don't have to worry about the stupid forward ref thing.
32:39 And now we don't have to worry about it. And actually, I decided to leave it with the more descriptive name anyway. So it works either way. But I just love that ref is no longer a special prop on your custom components, which I think is great. Okay, great.
32:55 So as a part of this, because now you're returning a callback that's a cleanup, If you were doing this sort of thing, which you probably were not, it's very unlikely that anybody was doing this, at least in modern code. But if you were, this is an implicit return of the instance, and so now you'd want to not have that implicit return, and they've got CodeMod for you to do that for you, which is kind of nice. Okay, great. Now let's talk about useDeferredValue. So useDeferredInitialValue is a new addition to an old hook.
33:31 So lots of you probably haven't actually used this hook much, but we've had it for a while. And they've just added an initial value to useDeferredValue. It will return it as the value for the initial render of the components. Sketches are re-rendered in the background with the deferred value returned. So actually, I think down here, oh, no, it doesn't.
33:51 OK. So the example of useDeferredValue is kind of like, I think it might actually be easier to show you the example they've got in their docs. Right here. Here we go. Nope, that is the example before, now we add useDeferredValue.
34:08 Okay, so here we've got some state, we've got our query, useDeferredValue, we're passing that initial, or that the deferred value, and then you get your initial value. I don't think that they've got an example of when you would use the initial value because pretty sure you just like, well, I'll explain the initial value here in a second. But Anyway, now this deferred query will, on the initial render, it will be the same as the query. And so you're rendering this, the value for our input is gonna be the query, but then we're wrapping our search results in a suspense boundary so that when search results suspends with like by making some fetch request and using the use hook and all of that, when that happens, we are gonna get the loading. So if you're doing a search and you're typing out, like I wanted to find the like information on the use hook, You want to leave the old stuff up.
35:06 Oh, no, why did you do that? You want to leave the old stuff up while the new stuff is being fetched, right? That just makes sense. But you also, at the same time, you want to be able to keep the query that the user is typing, keep that updated. And so you can't say, okay, set this query inside of a transition because that's going to prevent this state from being updated.
35:29 But if you don't put it in a transition, then this will suspend and you get this loading fallback. So what do you do? Well, you use deferred value. So what this is going to do is when this state change happens, React is going to render this twice. It's going to render it once with the deferred query being equal to the query, so the same thing that it was before, or the new thing, and then it will render it a second time with it being equal, or what it used to be, what it was before.
35:57 And so then, if the new render triggers a suspense, or a suspending state, then we can see the old stuff while we're waiting for that suspending state to be finished. I also have this in the suspense workshop. We go a lot deeper on that, but that's a basic idea. It's just, it's a mechanism for showing the old state while also being able to update the current state. It's kind of cool.
36:27 So the initial value, this would be useful if you wanted to pass a value that you want to defer, but you want to have that initial render be able to show like an old version. So you actually, on the initial render, you actually want two renders. Pretty sure that's what's going on with this initial value. Not something you're going to be using a whole lot, but cool, you'll like it when you do need it. Okay, let's talk about metadata.
36:56 This is a long and coming thing. So I remember one of my early examples of useEffect was updating the document title because you couldn't just render a title anywhere in the document. I mean, technically you can, but you want to put that in the head. Link and meta also should be put in the head of a document. Most applications, at least certainly back in the day, we were rendering our React app inside of a div with an ID of root, and that's where we put our React app.
37:26 And so the head was like off limits, and you'd only talk to it via JavaScript DOM APIs and use effects and stuff. Well, now you can continue doing that if you want, because now you can render the title and link and meta anywhere you like in a component, and those will get hoisted up automatically for you into the head. And the thing that concerned me first when I saw this, or when I was thinking about it myself, like, why don't they just do that? Oh, here's why. The biggest concern that I have for this is what about streaming and server rendering?
37:59 Well, They managed to make it work on the server. And so let me see. Yeah, it doesn't, I probably says, yeah, right here. Streaming SSR and server components, all of that works. And it's actually pretty cool how this all works.
38:15 Now, what does this mean for Remix folks who already have an export for meta and that sort of thing? It means you can continue to use meta, that meta export, if you want to. Some of the, yeah, it works just fine. I'm not sure that I would personally migrate unless the Remix folks were saying, hey, we're actually getting rid of this API, which they might for sure, like reducing the size of the API is an important thing for them. And for me as a user, But yeah, I don't feel super motivated to remove it myself.
38:51 But in the future, will I use this instead? Yeah, probably. One thing to keep in mind though, is if you have a suspense or something that triggers suspense right here, then these things will not show up in the head until after that suspense boundary is finished, which is probably fine. In fact, lots of the stuff that you would suspend on is probably getting data that you would need anyway. So anyway, kind of some interesting things to think about there.
39:21 OK, and they talk about you might still want a library because it helps with utilities and things that are specific based on the route, and like overriding generic stuff, and all of that stuff. So still could be useful. I think my guess is that in the future in Remix, the metadata API specifically for Remix will be removed in favor of just using React natively, which I think is a cool thing. Okay, great. So, let's look at support for style sheets now.
39:51 So style sheets are both externally linked and inline styles are a huge pain before in the good old days with React. And now React 19 is going to make this a lot better. So here, imagine this, or like the scenario where you put a link tag for some CSS that's specific. Yeah, here, we don't have to imagine this right here. You have some class names here.
40:22 Here are the style sheets that this article are going to use and stuff. The problem is that by the time this gets rendered, the article is already there and the browser has to go and fetch all of the link-styled sheets and stuff. So you're going to get a flash of unstyled content, which stinks. And so, what we can, and actually additionally, we also have this idea of the order in which you add these link tags is going to determine how they interact from a cascading standpoint. So if they have conflicting CSS styles, so like this one has class A and that has class A, whichever order you put them in is gonna determine how they resolve conflicts.
41:15 And so there are two things going on here. One is you have this precedence, which honestly, I'm not a super huge fan of. Anytime you have like some sort of order or ranking system, this is the Z-index problem, right? Like you have Z-index one, and then you got Z-index 100, and then Z-index 9999999, right? So we're gonna have problems with that, I expect.
41:39 Hopefully not quite so bad, but it could, yeah, that could be a problem. Maybe not quite so bad because we only have default high and I think there's probably a low as well. But yeah, you have, or maybe that's just the default is low or something. But in any case, yeah, we do have this precedence thing. But the other cool thing is this suspense fallback.
42:02 And so What this is going to do is, as React is rendering stuff, it's going to say, oh, you've got a link tag, and oh, you also have a suspense thing. We're going to suspend. We are not going to show this article until these links have been resolved, until those hrefs have been fetched and all of that stuff. And once they've resolved, then we will resolve the suspense fallback into the actual thing that you're rendering. So you will get a flash of loading state, but at least you won't get a flash of the unstyled content.
42:34 So this is interesting. I wonder kind of what it will look like on a server render. I think it will be probably great. It will hopefully be great. This, I think, will get hoisted to the top of the document.
42:49 And so by the time we get around to this, maybe we'll already have loaded. It will probably be awesome, actually. So actually, no, no. You hoist this to the head, and then that's a blocking resource. So yeah, in a server rendering scenario, this will definitely be loaded before the rest of the HTML is displayed anyway.
43:10 And so you probably shouldn't get a flash of loading state in a server rendering scenario. So I'm a fan. That's good. Okay, and yeah, so it's just during the client-side render, React will wait for the style sheets and we won't duplicate style sheets either, which is awesome. That is really, really great.
43:31 So you can render that component as many times as you like and React will be like, oh, I've seen this style sheet before. I'm not gonna put another one of those in the head. We've already done that. So that is cool. I'm a fan of that.
43:42 Okay, so we're doing lots of resource stuff. Like you can see, we still got quite a bit to go and we're, yeah, a lot of cool things that React 19 is adding for handling external resources, like scripts. So think about the Twitter widget or X widget now I suppose. I think it's still Twitter.com, But anyway, the widget, if you want to embed a tweet, they're going to give you the iframe stuff, but then they also give you, and it has like a block quote in it and everything, so you can actually see what the text is before the widget loads. But they'll give you the script in there as well.
44:13 And This is cool because it's just like copy-paste this thing and magically it'll work, but kind of limiting. And so what's really cool about this is they're going to dedupe some of those scripts. And in addition, React 19 is... So actually on that, you'd render like two Twitter widgets, and now you've got two of those scripts. It ends up working out anyway, because the browser's not going to render it.
44:39 But yeah, it's nice to dedupe those things anyway. So Yeah, we can render async scripts anywhere in your component tree, and like directly in the components that depend on that, without having to manage relocating and deduping and all that stuff, which is cool. So here's my component, you put them in twice and it only shows up once, which is awesome. And yeah, it handles this on server rendering and streaming and all of that stuff. And it sticks them in the head, prioritizes behind more critical resources that block paint such as style sheets, fonts, and image preloads, which is cool.
45:19 I just love that React is leaning heavier into the web platform, which again, it's doing this more with support for preloading resources. So when we're Initially loading, we have maybe a bunch of resources that we need to go and get. Some of those things we know exactly, okay, this is my header image, I need to go get that thing right away. Or this is my font, or this is some script that I'm gonna need eventually, not right away, but like pretty soon, or I'm gonna need something from here, but I don't know what it is, all of those things. So these are all actual like platform things.
45:56 So it's been around for a long time. You've got link rel, prefetch DNS, and preconnect and preload and all of these and loading scripts and everything. And so React has made it like you could probably do these things yourself, but now you also have an imperative way to do this as well. So you could do this in event handler or something too, which is I think kind of interesting. And they're I guess common enough that they want to give you this sort of API so you don't have to worry about rendering this.
46:24 So yeah, we've got our pre-init as script, so that will initialize that script. So it downloads it, should parse it, and execute as quickly as possible. Preloading fonts and style sheets, prefetching DNS so you don't know what you want specifically from a host, but you know you want something. This could save your users like 20 milliseconds or depending on their network connection it could save them a fair bit. It's surprising how much resolving DNS can take if it's a site that they've not gone to before.
46:54 And yeah, when you will request something but you aren't sure what, pre-connecting to a particular domain also can help and make things faster. And so it is nice that React is allowing us to do this in a more platform aligned way, which I think is pretty cool. Lots of resource preloading APIs. Okay, great. So let's, we're getting there.
47:16 Let's talk about compatibility with third-party scripts and extensions. Goodness, the number of hydration errors that we're getting because somebody's got a third-party extension that adds some styling to the page or something like that is a mess. I hate it. It is not great. The thing that is important to remember is that your app doesn't break for a hydration error.
47:42 It just gets deoptimized. And so when React currently, in React 18.3.1, as React is going through, if it sees something that's different from the server versus what you're rendering on the client and hydrating the server HTML with, it sees something different, it's like, whoa, okay, I don't know what to do with that. So we're going to start over and render the client all over again and switch them, switch the current HTML with what we're rendering so we don't have to worry about who knows what event handler issues we'd have or whatever. And so The problem is that third-party scripts and extensions will modify the head and it ends up being a big issue. So this should resolve a lot of problems in that regard.
48:29 So while hydrating, previously inserted by third-party scripts or other extensions would trigger a mismatch error. Now unexpected tags in head and body will be skipped over, avoiding mismatch errors. If React needs to re-render the entire document due to some other mismatch, it will leave in place style sheets inserted by third-party scripts and browser extensions. So, still possible to get hydration errors, but the only hydration errors you should get are the ones that you caused, which I think is great. And on top of that, when you do cause a hydration error, we're going to get much better error messages as well.
49:05 So thank you. That is nice. And we saw earlier that's what those error messages look like with the diff and everything. In addition, like in general, there are errors that are thrown. You'd get two errors and then you'd get a third log that says where the error happened.
49:23 The two errors are because React tries to recover when there's an error and if it doesn't automatically recover, then it gives you that second error. So now it's just going to show the error once and it's all going to be in one error, which is nice. On top of that, these are some APIs you probably have not used before, but in CreateRoot and HydrateRoot, you've got a couple of options that you can provide. One of them was onRecoverableError, but now we also have onCaught and onUncaughtError, which can be quite handy. If you server render something and that server rendering throws an error, then your suspense boundary will be rendered instead.
50:09 And then on the client, they will try to render that again. And if that fails, then it will render your error boundary on the client. And that is considered a recoverable error. There are situations where you actually are planning on that to work that way, and so that's kind of annoying. And so you can opt out of that with onRecoverableError and just say, don't log in this particular case.
50:31 We actually do that in what I think the suspense workshop in Epic React V2. So look forward to that. Okay, and finally, we've got support for custom elements. Not something that I'm personally excited about. I'm just doing React everywhere, but I expect that there will be a lot of people who are really excited about this.
50:53 People who are working at, like when I was working at PayPal, not everybody was using React. I think maybe most everybody is using React there now. But Some people wanted to use other frameworks, and so having a component library of web components was really attractive in that world. And you could create a component library of web components that work in all the different frameworks, and now finally React has proper support for web components, which is awesome. Shout out to Joey for building that.
51:20 So that's super. Okay, so we're at the end of the blog post, but I think that they missed one big thing I wish that they would have put in this blog post. And Maybe the reason that they didn't is because it's just so obvious. I don't know. But let's look at...
51:39 We're doing refs, mock pattern factories. Yeah, that's gone. Oh, right there, folks. We're removing React test renderer shallow. It's gone.
51:49 So you can no longer use that. And now they have a separate package for it if you really, really want to do that, but don't. It's so bad. Don't do shallow rendering. Instead, you should use testing library.
52:03 Oh, I saw that and I was so, so excited. And then, what do my eyes behold? They're removing testutils from react-dom. What is this? So now, they've moved act because act is still necessary, but they moved act into the react package directly.
52:24 Test utils is no more. And that is because testing library is the way that they want people testing now. And that is awesome. Super, super awesome. I thought that, oh yeah, here it is, deprecated React test renderer.
52:39 Oh, because you should be using testing library. Oh my gosh. Yeah, finally. So, so validating. So some of you may not know, but I created Testing Library years ago when everybody was using React Test Renderer or Enzyme and doing shallow rendering and all of this stuff.
53:00 And I was like, no, you're all wrong. We need to do this differently. Created a testing library, and now it is the way to test React components. I'm very, very happy about that. So there are a number of other changes in here.
53:15 I just scrolled past the UMD builds are being removed and they recommend ESMSH. So that is interesting. Various other things that you could take a look at in here. But I am super, in general, super stoked about React 19 beta. Hopefully later this year we'll get React 19 proper as a stable release.
53:37 React doesn't slow down, folks. React is just the sleeping giant that you come and find out, oh, the giant wasn't sleeping, he was cooking. And now like so much of awesome stuff is coming to React 19, very minimal breaking changes. I mean, removing a test renderer and all that stuff could be a pretty big breaking change if you rely on that. But it's been like four or five years since testing library kind of became the standard.
54:06 And so I like that React gives people plenty of time to migrate to the new way of doing things before removing stuff. So that is React 19. Thank you for joining me on this journey to explore what React 19 has to offer and we'll see you on the internet you epic web devs. Bye!
- Play Replace Remix's unstable_parseMultipartFormData with @mjackson/form-data-parser
Replace Remix's unstable_parseMultipartFormData with @mjackson/form-data-parser
- Play Epic Workshop Diff Tab Demo
Epic Workshop Diff Tab Demo
- Play Epic Workshop Test Tab Demo
Epic Workshop Test Tab Demo
- Play Get Started with the Epic Workshop App (for React)
Get Started with the Epic Workshop App (for React)
- Play Prisma Typed SQL Queries
Prisma Typed SQL Queries
- Play A Deep Dive in Tailwind Font Settings
A Deep Dive in Tailwind Font Settings