943: Modern React With Ricky Hanlon ![rw-book-cover](https://wsrv.nl/?url=https%3A%2F%2Fmegaphone.imgix.net%2Fpodcasts%2F5197fe5a-42f7-11f0-affd-87d9985a1760%2Fimage%2Fc86a54acd72683732c4773e25bf0ae14.png%3Fixlib%3Drails-4.3.1%26max-w%3D3000%26max-h%3D3000%26fit%3Dcrop%26auto%3Dformat%2Ccompress&w=512&h=512) ## Metadata - Author: [[Syntax - Tasty Web Development Treats]] - Full Title: 943: Modern React With Ricky Hanlon - Category: #podcasts - URL: https://share.snipd.com/episode/8205033e-5dd6-415a-9b76-4f1be0ebf4af ## Document Notes ## Highlights - **Transitions Are Scheduling Primitives** - Transitions are scheduling primitives that let you prioritize UI feedback over longer updates. - They coordinate work so user input remains highest priority while heavier updates run later. Transcript: Ricky Hanlon Yeah, you can think of a transition as a UI transition. Fundamentally, it's kind of like you can think of it as maybe a little bit like a transaction or like a background thread. So in native apps, you have a problem where you handle input events on the UI thread. And that can cause stuttering and laggy UI if you do everything on the main thread. So it's very common to offload expensive things to a background thread. In JavaScript, we don't have a background thread. Everything is main threaded, right? And if you go back to, I was actually just watching the conference talk for React Confident in 2017 when we first introduced Fiber. And that talk is really great because it talks about all of the problems for performance in apps and kind of walks you through the conclusion that the core concern for UI libraries, for Innovating in the future and being able to handle this problem. Handle this problem is scheduling. Being able to schedule different types of work like transitioning and transition being like navigation or updating state or data on the server and then navigating or refreshing, Pull to refresh, right? These are all scheduling problems. And so the fiber rewrite all of the features that we've been working on with like the big scary concurrent mode name, the concurrent features are really just scheduling features. How can you prioritize user input and defer transitioning the result of that input to kind of like a background thread so that you can always handle user input as the highest priority. ([Time 0:03:20](https://share.snipd.com/snip/2f323520-b01f-4a62-b05d-ceedb160ecd5)) - **Give Immediate Feedback, Defer Results** - Show immediate UI feedback for user interactions and transition the heavier result asynchronously. - Update small visible state synchronously and defer expensive fetches or rendering to a transition. Transcript: Ricky Hanlon Yeah, that's a that's a good example. I think like historically, we've given that example, maybe too focused on that example, because that example is generally explained as being like CPU bound, right? If you're like filtering a really large array, then as you're typing, you don't want the the array or rendering the items to take too long. But if you think about like a type ahead, the primary way that people would do that is with a request, like an async request. Right. And so how do you coordinate all of the different requests that you're going to be firing off on like each keystroke so that you're not showing old results. One of the things that drives me nuts about UIs is when you are typing in a search bar or a type ahead, and then just as you go to click the result, it changes in the order, and you click the Wrong one. Ideally, transitions can help you fix this because you'll be able to batch all of the requests and only update the results once. I think another great example is if you have a table and you have different filters on the table. One thing that's very common to do, especially if you're syncing it to URL params, one of the things that's common to do is you click a dropdown, you click a filter, like filter out these Items. And then one of two things could happen. If you're syncing it to the URL state synchronously, then you click one of the items and then the dropdown closes with the old value, not the ones that you selected. And then there's some time, maybe a fetch or something, and then the results of the table update. So that's a bad experience. What you want is to immediately update that filter to show what the user clicked, like show feedback for the user interaction immediately. And then you can transition the results to the new results. ([Time 0:05:27](https://share.snipd.com/snip/f8358300-3a3b-4dcc-ba53-7236b5da15d3)) - **Deferred Reads Versus Deferred Writes** - useDeferredValue defers at read time while startTransition defers updates at write time. - Suspense batching and throttling help group updates to reduce DOM reads and improve animation timing. Transcript: Ricky Hanlon Related to it? Use deferred value I think of as kind of like a way to defer at the read point instead of the write point, where a transition is like when you're at the write point, you're in the click handler, You know that you can defer this state update or defer this, um, mutation. And most of the time the, that right point is happening either in your data fetching layer or in your router, right? You, you shouldn't need to, if you're using a suspense enabled router or data fetching, you shouldn't need to wrap your like router.navigate and start transition. Router.navigate should happen as a transition because router navigation events are always transitioning the UI. But a lot of times you'll be at the read point that you need to be able to defer part of the UI so it doesn't block user input. And that's when you would use deferred value. And then suspense has a lot of things to it. The thing I really am trying to help people appreciate about suspense right now is the batching behavior. So a lot of other frameworks that are out there say like have suspense now, but I think they should have like, we have suspense asterisk, which is like, we have a component that shows a Loading state, but doesn't do everything that react suspense does. So for example, we do a very controversial thing called throttling, which is if you are loading a page and we show a fallback, we make sure the fallback is shown for at least 300 milliseconds. As soon as the data is ready, say in 10, 50, 100 milliseconds, it feels slower than waiting 300 milliseconds to the user because you didn't have a chance to see like the fallback content, Right? All it looked like to you is just jank. And so we throttle, but the throttling gives us a lot of performance benefits. ([Time 0:08:30](https://share.snipd.com/snip/dab9a32a-7c7a-4aaa-a1d7-3f5d29c4fb92)) - **Throttling Reduces Flicker And Layout Work** - React throttles short fallbacks to avoid flicker and to batch updates for one DOM layout pass. - Batching reduces multiple animations and layout recalculations, improving perceived performance. Transcript: Ricky Hanlon So a lot of other frameworks that are out there say like have suspense now, but I think they should have like, we have suspense asterisk, which is like, we have a component that shows a Loading state, but doesn't do everything that react suspense does. So for example, we do a very controversial thing called throttling, which is if you are loading a page and we show a fallback, we make sure the fallback is shown for at least 300 milliseconds. As soon as the data is ready, say in 10, 50, 100 milliseconds, it feels slower than waiting 300 milliseconds to the user because you didn't have a chance to see like the fallback content, Right? All it looked like to you is just jank. And so we throttle, but the throttling gives us a lot of performance benefits. So for example, if you don't throttle and you're like doing something like reading layout in the dom well then you're going to potentially have multiple updates and that's multiple Reads of the dom and multiple layout computations but if you were just to wait and batch everything and commit it all at once then you'd only have one layout oh so that that 300 milliseconds Scott Tolinski Gives you 300 milliseconds to wait around and see if anybody else, anybody else want to update state or whatever in here? Yeah. Ricky Hanlon And that's especially important when it comes to animations, because for animations, you want to try to minimize the amount of fallbacks that you animate in. Right. And so if you were to trigger an animation, if let's say you have like two items in a feed and you are replacing those items with the content as fast as possible. And each one's triggering the animation. Well, if you're using document start view transition, you can only run one animation at a time. ([Time 0:09:44](https://share.snipd.com/snip/e05db129-bb96-487b-b016-10915c5b9267)) - **Show Skeletons Quickly** - Show a skeleton quickly after user input to improve perceived performance. - Users tolerate skeletons up to ~500ms and blame network, not your app, for that delay. Transcript: Ricky Hanlon Skeletons in particular are very well researched. And research is basically like the faster you can show a skeleton, so there's no delay from user input to skeleton. That matters a lot. The amount of time that you show a skeleton, like a suspense placeholder, doesn't matter as much up to a certain point of like 500 milliseconds. Because the user is going to blame that on their network. You're not going to blame that on your app. If your app flickers, then that's a programming problem with the app. Like the user feels like the user is accustomed to being able to like wait for server data. ([Time 0:12:56](https://share.snipd.com/snip/1a89f132-e33b-4f10-b6e5-c0ae498ac6b0)) - **Push Transitions Into Framework Layers** - Prefer routers, data layers, and design systems to implement transitions and suspense by default. - Let frameworks handle scheduling so product code stays simple and gains UX benefits automatically. Transcript: Ricky Hanlon Yeah, I think a lot of the talk that I'm going to be doing at ReactConf is like, what I'm seeing a lot in the community is like a lot of product code that's like needs to wire up the transitions Correctly and use use optimistic correctly and use all these like new hooks in the product code and and you got to use it all correctly but i think in the way that i i do a bunch of side projects And the way that i write my apps in the side projects is that the product code just has like an event handler and in that event handler, I'll just call a navigation, or I'll add a wait, save Data to server, and then navigate. I don't write all of the transitions and the modern features, and even suspense. Suspense is behind some design component. I don't write all those features in the product code of the surface itself. And so I think what I see shifting in like over the next few years is building these features in by default in three areas, the router, the data fetching later, and the design components. Those places, then you just get these experience for free by default. ([Time 0:13:53](https://share.snipd.com/snip/1e344db5-4ed8-40c4-a7ca-067e3617b3f3)) - **Use Optimistic Updates For Instant Feedback** - Use useOptimistic to show immediate temporary state (e.g., like button) before server confirmation. - Treat optimistic updates as high-priority user feedback and later replace with transitioned data. Transcript: Ricky Hanlon So use optimistic I think is initially hard to wrap your head around. But I think it's a little it's sounds more complicated than it is. Use optimistic is a hook that allows you to show some temporary state until the real state is available. So the classic example i think instagram was one of the first apps that really like used optimistic state pretty heavily like way back in the 2000s and they would use it for like the like Button when you hit the like button it would immediately show that it was liked and then it would kick off a request in the background yeah so it would feel really instant you want to like Hit the like button see a pending smitter or even worse, they even have a pending spinner. It just doesn't do anything until it like hits the server. And so use optimistic is something similar. It's like, it's, it's part of that user feedback portion that I was talking about earlier, where you want to immediately show user feedback and then handle the result and transition To the UI to the result. And so actually for the use transition hook, the is pending flag in use transition is implemented as use optimistic. ([Time 0:15:42](https://share.snipd.com/snip/8310823e-b473-424e-be21-1ae559249087)) - **Rely On Suspense Heuristics, Not Timers** - Avoid manually tuning suspense delay heuristics; rely on React's built-in heuristics instead. - Use suspense boundaries when you expect slower content and let React show fallbacks immediately. Transcript: Ricky Hanlon That was added in the like experimental phase even before React 18, which is before I joined the team. So I don't have a ton of context, but which is kind of wild now, like the before I joined the team is getting longer and longer ago. I think that the reason that we removed that was that it was too hard to do it correctly. Tinkering with all those settings and getting it to work correctly was difficult, and we replaced it with heuristics. So for example, if you want to hold back the transition until the content is ready, don't use a suspense boundary. If you transition to a part of the UI that doesn't suspend, then the transition will keep you on the previous page until all the data on the new page has loaded. But if you do specify a boundary, you're kind of saying like, this is not going to load fast enough. I need to like show something in its place. And that's where the user research comes in where you want it to immediately show that fallback. ([Time 0:18:13](https://share.snipd.com/snip/05b54b87-024c-441a-9110-889d8ff0de6c)) - **Prefetch On Hover And Cache Promises** - Preload fetches early (hover or mouseenter) so many requests finish before navigation. - Use cached promises or a data-fetching library to suspend reliably and avoid late updates. Transcript: Ricky Hanlon I'd say do what works for you. But if I was writing an app, I would use Dispense. I would use use promise, definitely preload the promise. So for example, if you're navigating on a click, then you want to start the request as soon as possible. Ideally, in a lot of cases, start prefetching in the hover event. ([Time 0:20:12](https://share.snipd.com/snip/662df450-91c0-4d1d-a42a-61d8371efcb4)) - **React Is Not Owned By Any One Framework** - React builds features for broad use, not to lock them into a single framework like Next.js. - Core work aims to make features usable across frameworks rather than proprietary to one vendor. Transcript: Ricky Hanlon People are saying. That's what they say on Twitter, right? Well, first I'm going to get a shirt that says cash rules everything around me. Exactly. Yeah, right. Yeah, no, I hate that question. Thanks for asking it. Yeah, I love it. Wes Bos I knew you would. I'm sorry. Ricky Hanlon No, I think Dan said of us, we kind of still just work as the React team. And we build all of the features. The thing is, if you imagine what that would look like of like, oh, they're taking it over and like they're built and they're making it for their own. Well, we wouldn't have just let everybody use all of the features that they can use like yeah server components would be completely locked down we've just built them we could build all These features only into next and we put so much work into like building them in the right way for anybody to be able to use that uh it almost like, why do we go through all this work if it would Be have been way easier to do it? ([Time 0:22:10](https://share.snipd.com/snip/dbd06045-5adb-4cc9-8386-6c3467f2e6b8)) - **Concurrent Store Research Underway** - The team is exploring a concurrent store API to support transitions across external stores. - Sync external store currently opts out of transitions, so a new API is needed for concurrent updates. Transcript: Ricky Hanlon Don't know if it's going to be signals-based, but we did share in our labs post that we're starting to think of a concurrent store API. Right now you can only use sync external store, which opts you out of transitions. It is a hard problem. I've been thinking about it for a long time, like being able to sync something concurrently across multiple conceptual threads is difficult, but I think we'll be able to do it. I don't want to share too much because I don't want to, you know, you make, you share too much and then it changes. ([Time 0:23:33](https://share.snipd.com/snip/506157fd-c295-44b4-bae1-235a4d3b2677)) - **Canary Rollout And Hydrogen's Feedback** - React shifted to a canary process for building more in public with earlier adopters like Next and Hydrogen. - Hydrogen influenced server components design, prompting directive-based changes such as .client/.server patterns. Transcript: Ricky Hanlon Yeah, I think I think that there were a lot of lessons learned. Server components kind of overlapped with a change that we made to build more in public. So before server components, if you think back to like hooks or any of the features before that, we would just like build it almost secretly, ship it at meta and get a bunch of feedback From meta engineers and nobody else would outside would be able to use it and then once we felt it was pretty good we'd cut an rc and then everybody would start like integrating with it That wasn't great because it was like we were only being able to kind of build to a small subset of the community and so we changed to the canary process where we would kind of like do a lot Like what browsers do with um like tc39 standards whereas something like progresses it gets to a point where it's like ready for implementation it's not in the standards yet but it's Ready for browsers to start implementing it and running origin trials and stuff like that and so that's kind of like what our canary process is i think that there was a lot of confusion Because that was the first time we built something that way it was like expected that like as we were sharing it it was more like ready for like um ready for like done it's like oh it's done And only next has it. But really, it wasn't, like, done until React 19. It was stable and ready to use in production, and Next was able to ship on it. And one thing that people also forget when they talk about server components, because they're like, oh, they only built it with Next. That's not true. Have you ever heard of, like, Shopify Hydrogen? Hydrogen was built with server components. I don't remember the exact timing, but I think we built it in Hydrogen first. Or at least at the same time as next. And they gave us a ton of feedback. They gave us feedback way back early in the day of the original server components. Naming didn't use directives. They used file extensions where you'd have like.client and.server. And when Hydrogen was shipping it and trying it, they gave feedback that it wasn't working. And like the shared component thing didn't really make sense. And that's when we went with the directives model instead. But they were directly. So we were trying. ([Time 0:26:01](https://share.snipd.com/snip/d93a5695-b3de-48ac-980c-7274e594f371)) - **Priorities, Not Time Slicing** - The real benefit of concurrent features is priorities and coordination, not time-slicing rendering. - Coordinating requests, animations, and batching produces smoother UX even with fast renders. Transcript: Ricky Hanlon And then the router can just do all of that automatically for you. But I think this was a great demo because one of the things that's interesting about this demo, a lot of people when they talk about transitions, we're like, oh, it's time slicing. It's like because React is slow for rendering, so they need to like split it up so that it still feels responsive. All of the rendering chunks that were happening in that demo were like sub like a couple milliseconds. The rendering itself is not the benefit. It's the priorities and the coordinating all of the async requests and coordinating all of the animations. Those are the benefits of all the new features. And those benefits are something that you would want in any UI library that you're using. You want to do things like batch stuff so that it animates together. You want to do stuff like defer stuff in the background so that you can still fire off network requests and still keep the DOM around. But if you're using signals, your signal updates don't need to go and immediately update all of the DOM for stuff that's not visible on screen. You want to defer those updates. ([Time 0:32:54](https://share.snipd.com/snip/a9f6a703-0085-4414-bfa0-af38d8e741ef))