Video details

Sendil Kumar - Designing High Performance React Applications - JSWORLD Conference 2022


Sendil Kumar - Designing High Performance React Applications
Sendil Kumar talks about Designing High-Performance React Applications and is an Engineering Manager at Uber who Loves Open Source.
Sendil also wrote an amazing book on Practical WebAssembly. In his talk Sendil talks about Why Web Performance Matters. Understanding Core Web Vitals, Rendering and making use case decisions.
Like to be kept up to date about all JSWORLD Conference Videos?
The No. 1 JavaScript Conference on the planet is a bi-annually JavaScript Conference brought to you in Europe and the United States.
#jsworld #jsworldconference #frontendlove #frontenddeveloperlove #javascript #reactjs #angular #react #vuejs #vue #vue3 #typescript #graphql #jamstack #amsterdam #conference #svelte #sveltejs #next #nextjs #staticsite #css #html #nuxt


Hey folks, how are you doing today? All good. How was the lunch? They switched off the light too. It's easier to sleep now. Great. OK, welcome to The Talk designing high performance react applications and the why I chosen React over here because it's top on the list every year and it's something that we use day in, day out. I just want to go through what are the things that you can do to improve the performance in the applications that you have. Just an introduction. I'm Sundel, I'm an engineering manager at Uber and I love open source and also contribute to many projects and I've recently published a book on practical web assembly with packet. This is a discount code there if you would like to read about web assembly and know about it, you can use this thing. And also we have a booth behind the black box and you could win Vouchers over there. So just go ahead and check out what we're doing with that. The talk. Why performance matters. Why do you think performance matters? Any answers? Anyone? User retention? Great. Anything else? One more? No? Okay, let's go here. User retention is one of the bigger things, right? And also if you take your performance of your application for every second that you're adding into your application loading time, you're going to lose a lot of uses from it and that causes a lot of issues for you. And for example, this is one of the studies done by Valmat where like one to 4 seconds is an ideal place for your application to load and show content to users. The moment it crosses that four second mark, look how drastically your application loses number of users who are going to use and who are going to use your product or buy anything from your website. And that's critical. So every 1 second improvement that you're going to add to your website is going to increase your conversion rate by 2%. That's not small, it's huge. For example, if you're doing 10 million run rates, it's going to add in 200K revenue more to you and that's a huge thing that you're going to increase. And ideal load time for any website is three to 4 seconds, but average mobile site speed is around 15 seconds and it's a long time and we have to shave those little bites of information that we're sending to the browsers and enable that we are having more consistent and a better performance. And that's the main reason why performance matters and faster sites is providing happier users. Happier users which means higher conversion rate. And there are a lot of things that you can see on the web dev. It's a great resource for learning about what are the impact that a good performance can have in your application. Please do check that link and check how different companies try to optimize performance and get more conversion rate for their users and things like that. And just before we jump into performance is very essential for us to understand the web vitals or the things with which we are going to measure the performance. And that's how I see it. So for example, if a user wants to load your index HTML, they first get the home page and it gives you home HTML or index HTML. And then what it does is like it has some JavaScript in it and the browser is going to download the JavaScript. And this particular instance where you're getting the first bite is actually even before, like when you're getting your first byte of the HTML. That's what it's called this time to first byte. When a user loads your resource, the very first bite that reaches from your server to the user. And that's the entry point for your application. And once you get your JavaScript, the application will get loaded and your application, you'll have some visible content over there and that's your first console pane. That's when your browser starts to do the paint of everything that you kind of have there. And then finally the rendering of your application happens. And once your application rendering finished, that's what we call as large as Contentful Paint or LCP. And finally that's once LCP is completed, then you will have this TTI which is time to interactive or interactivity where your application is usable for the users. And the biggest thing is what is the time difference between the SCP and LCP is basically determines the performance of your application. So from the beginning till the end, how much time is your application taking? That's where the performance matters. And you have to make sure that the number is much lower. The lower the number, the better it is. And also it's important that when you're doing performance on everything, when you're loading your application, it's very important that you have to think about layout chips. Layout chips are very essential because your application might load and it might change once the LCP is completed. Like once NK application is loaded, your application might look differently in the browser. And when that happens, it will have a lot of problems. So layout chips is also something that you have to take a look into it. So first, faster, largest content will paint, sooner time to interactivity and lesser layout chips is what you're going to target if you're going to build a high performance application. So how are you going to do it? In this talk, what you're going to see is like the first thing, obvious things, the things that you possibly know and the things that everybody should do. And then we'll go for the rendering part of it. Rendering is very essential and rendering determines how things are shown to the users and how we are going to show it. How you can give better performance look alike if you want to give it. And then finally some things it depends on the use case that you're working on and that's what we are going to call it and I'm going to call it as it depends. And finally the common things, it's not only for the react application but you can use it in anywhere, any location. So how you can do this and the obvious thing, the first and foremost thing is bundling and splitting things together. And we have like awesome bundlers, everybody uses them today, web, pack, powers or anything else. The bundling and splitting forms the basis of everything that you're doing. So what it does, it means so you have this home, you have the main dot JS, the same thing that we've seen before and your render application, the JavaScript that you're seeing here, it can contain the Nt application or it can contain just a part of the application and you can determine that using the bundlers. So a browser has to wait until the main JS is completely downloaded, there is no streaming compilation in the browser for the JavaScript, which means it has to wait until the entire JavaScript is downloaded. And you can take an intelligence step here, try to reduce the number of bytes that you're sending in for this particular thing and make sure your application loads faster. The smaller the size of the JavaScript that you're sending into the browser, better the performance will be and that's where like webpack and all those things can help you there. And you can also split it based on route based or also on component based splitting and you can take the step based on the application that you have where you want to do the spreading. That's very essential, that's very basic. And then you also have to budget the JavaScript that you're sending in and ideal world it's like 100 to 170 JavaScript file is ideal for doing the transmission and making sure the site slides between the four second mark below the four second mark. So always try to budget the JavaScript that you're sending into the client, make sure that is solicit and also take some time, analyze the bundle, try to optimize it, remove unused things that you're going to have it there and make sure that what you're sending to the client is actually what the client is requiring. That's basically what you have to do. The next step you can do is touch on how you can make your application faster on your side like bundling and splitting. But when it's loaded on the browser, how can you make it faster, how can you make it look like faster? And that's where data mutation comes into picture. Trying to avoid data mutation most of the time because every mutation that you're going to have in a react application. It's going to cause a reentry and whenever there is a reentering that impacts the performance. Whenever there is a rendering your component heights and it comes back again and how you're going to make it shorter and how you're going to reduce it actually determines a lot about your performance. And also Immutability is your friend because it's very essential to have your application to use Immutable components and Immutable props and state to make sure that it's easier for you to reduce the number of no reload that is happening over there. And then the React Well reconciliation is also one of the things that happens. I hope everybody understands how react that's the reconciliation over here. So you have this simple component tree from the React well, you have the root at the top with all the leaves bottom. You're doing an update, something changes on a component here. Then what happens is like React calls and should component update for all the children of that component which got updated and the should component update says like hey, these are the fields that have been changed, these are the components that has been changed. You're going to change these things together. So whenever should component update returns true, those are the components that one lead will get rerendered, the other components will not get rerendered. But React also does those things. Like next it compares the virtual dom. So it has a virtual Dom in this memory. It goes ahead and compares that virtual Dom to the components that it has before the previous state and the new state. It compares that whenever there is a change, only those components will be rented, other components will not be rented. And it's very essential for you to understand this thing. So only those two leaf components that we have at the bottom gets rendered because it's Dom, virtual Dom and the Dom the new domain. Virtual dom is not equal. That's how it works. So think about this an architecture code application such that the readering affects lesser components in the tree. So whenever you design your Reactory or React componentry, react application based on the components that you're going to build, make sure that all the components does not affect pretty much on the other components and there are no side effects involved there that's very few components and things like that help you out. And finally we can also utilize this act of virtualization react does help there with a library. So this is what we call as a viewport. Anything that falls between the browser header and the footage of the browser, that's called a viewport. Always worry about the things that you're going to show in that viewport. Do not worry about the things that kind of like you're not going to show it, you're going to have it and you're going to show it later. Do not worry about it and try to make sure that the next thing. The very next thing that you're going to do. Make sure that you're optimizing. That you're preloading those content and have it in your site such that it helps you to make sure your application is much faster and it understands how it should work and there are numerous studies that have been found on the advantages of virtualization. It has like more than 100 times your performance impact where if you're just loading ten k rows in the Dom there is a source attached to it. You can also look back once the site is published but it says you have enormous performance impact just by using virtualization. So think about this, whatever that lives in the viewport, optimize and make sure that you have smaller content there and whatever that comes right next to that, make sure that you're preloading it so you can have more snappier performance. And of course Reactivating comes with automatic batching, right? And so batching is like when multiple state updates happen, React groups them together. It kind of happens in React 17 and any versions below React 17 too. The main difference is that if you have quote like this where you have steady state and also you handle click function, the reentering kind of happens here, kind of happens here and when that happens inside and asynchronous context, the only difference between this side and the before slide is it happens inside the asynchronous context. Whenever it happens inside the asynchronous context, reentering happens at every state update. So whenever you do a state update, reentering will happen there. But from React 18 what happened is they kind of remove that condition. So any state updates that are going to happen inside on synchronous context, it will also be bashed and grouped together. So if you are working on React 13 and you need some performance impact, a better performance in your application, make sure you're updating to React to team what it does give us better performance, no unnecessary rerenders. So it kind of like eliminates a rerender whenever you do state updates between the asynchronous context it also gives you a very good consistent user experience for users that will not be weird intermediate state so it is much more better user experience. So on the obvious side, we are going to decrease the bundle, we are going to decrease the number of renting that's going to happen, we are going to decrease the number of updates that we are going to give to the component. Try to visualize as much as possible and if possible use the new features that we have in React which kind of helps you to showcase a better performance and always ensure that you're delivering only the thing that is used by the application. Try to make sure you optimize and remove all the unused resources completely. So the next part of it is rendering and how do you render actually affects and showcases and makes sure like even though you have a much bigger bundle, much bigger size of code and things like that, rendering actually helps you to achieve your performance in a much better sense. And there are like two types of rendering that we can talk about. One is plain side rendering and it's nothing but a static rendering. So in this world you have server where you have all your files and then you have the browser and you just render everything together there. So it just goes in the audio and it's very good for these kind of static components like header or the footer on the sidebar or anything that you have which is much more static, which you just loaded once and then send it to you, send it to your users, you can put a static rendering for them and then they can just ride a vacated from there. What it helps you to achieve, it gives you faster first time to first bite. It's really good for static contents and it's very good for that. But the performance actually depends on the client. If you're sending a lot of JavaScript, your network speed matters and the browser in which the user is working on also matters to make sure your application is looking highly performant. And the time to interactivity is also high. If it's a low end budget device, the time to interactivity will also increase and the budget size increases. The file size of the budget increases with that with the client side renting because you're just chunking everything out and throwing it to the users. On the case of server side rendering where SSI generates just in HTML and from the react components in your server and then sends it to user. So in this case you can actually rehide it. For example, if this is the end state that you're looking for your application, the very first thing as I started it first loads the entire thing it starts to load. Once it starts to load, it goes and tells your server hey, get me those files. And then your server fetches the data that is required for your components to show and then renders the HTML. Once it renders the HTML, it sends HTML rendered HTML to the browser and in the client it receives the HTML and starts to load it. But at this point you'll just have HTML. It will not have any interactions, only HTML kind of interactions are allowed like link ticking and things like that. Finally, what should happen is hydrate. So hydrate till then your application is not interactive. Only when hydrate happens, your applications become more and more interactive. And it's very essential to understand in the world of SSR where it has to finish the fetching first before you can start the loading. And it has to finish the loading before you can hydrate the components and it has to finish the hydrate before you can start interacting with their components. It gives you almost faster time to interactivity because the time taken between your HTML component and hydrating those components is much smaller. So it gives a faster time to interactivity. It's very good for dynamic content. The performance doesn't depend on the client because all the things that you're doing is on the server and the time to first buy the site because the rendering actually happens to the server. So it has to take a little bit of longer time than just sending it out the content. It also gives you a high server cost for you. And you have to balance this thing. It's not SSR is always good or CSR is always good. You have to balance as you want. And reaction comes with suspense and SSR. And that's also some cool feature that comes in where Suspense lets you go to wait before it can load. So what you can do is consider this is a component structure that I'm going to have. I have a header sidebar main which has post and components in it. So what I can do is I can make sure these two elements, these two components are serverside Render. Okay, these two are serverside render. And then right now, if it has to work in my server, I have to fetch the post. And then for every post, I have to fetch comments for that post. And that takes a longer time. But what you can do is I can wrap comments inside the Suspense component and then now I can fetch force separately, send that data to the client, and then I can fetch Cummins for first. And then I could wait for this thing to load later and then build it, which basically eliminates the need of finishing fetching. I could just fetch whatever I have and throw it to the users. I can load the things that is available over there. I can hydrate the things even before complete loading is completed because Suspense lets you give a sample component. And finally, you can also have an interaction there. And rendering is very essential. And there is this new concept called Islands Architecture. If you're really interested, just go ahead and learn about it. Islands architecture gives much better picture of how to generate your application better. And also there's this school talk by Lydia. The source is also over there. And this is really awesome talk if you want to deep dive into rendering. This has a lot of information over there. And finally, the next part is it depends. And again, it depends if you're in the world of like where you want to have high performance, server components will also help you out. So several components are the components that will run on the server and fetches it. It's the next level of server side rendering. I would say it's not at production ready, so be aware of that. So you can define two kinds of component. One is server JS and a client JS. And then you can have the normal component, JavaScript. So server JS runs only on the server. Client JS runs only on the client. And JS, you can put it anywhere. So here the server JS, when it renders on the server, it goes and connects with all the essential stuff and then it gets all the data that's required since it's closer, it's much faster and then all the libraries that you're going to put it inside the server JS will not come to the client side. So for example in this case you have around like 1.2 MB of data. You need not wait for calling the database from the client. All those things will be happening on the server side itself and you calculate them on the client itself and then finally these things are handled and the SSR HTML is only that is sent to you back. So that is very easier for you to handle everything together. So here you avoid the reason of having higher dependencies props are allowed and you can also import client or JS pen and where it's necessary. So it's really nice and also use the technologies as you need it. That's the next one. Try to not over complicate certain things, not trying to add like redux or CSS and JS or GraphQL and things like that just because the fanciest of around you just think about it whether you really need those and then add those which kind of drastically reduced amount of JavaScript that you're going to use. And also limit your component size. For example, this is a tree that we have seen before. What if I coupled everything that I have on the right side into a single component? Then whenever we have updates certain things it has to rerun the entire component that you have over there. So try to optimize that and try to reduce the size of the component that you have. This is kind of an ideal structure where you can reuse most of the things. Finally, the common part cash is your friend and you have to enormously use those cash if you're going for server side rendering or on the other hand server components cash might not be that much useful but in the case of clients that are rendering most of the static components that you have try to use more caching there. Caching really helps you to elevate the performance for your application. So as you all know, in the world of cache you have something rendered on the HTML is there. So whenever you call it from your browser, the HTML will come to your edge where it will cache and then you can put it over there. So the rendering speed and everything that you have the performance also will be incrementally increased over there. And the next part is like trying to make sure even if you're caching go to the next stage and try to put your caches across the globe. If you have customers worldwide, try to put it across the globe so that the shorter timeline you reduce the network latency that is there and ensure that your applications have better performance and better things. And the next part is like try to load your images lazyly because images are everywhere and you're going to use it anytime and make sure that you're using lazy loading on images. So this is a normal image, how you render image in your HTML. You can go ahead and either load it automatic or you can make it eager, which kind of forces the browser to wait for the image downloading to. Or you can also go for image lazy which makes it much more simpler and it loads images much smaller, much faster because it can lazy load. And this is the Apple MacBook site. If anybody has seen this, they kind of load around 17 five megabytes of images for that single page just to show you Macbookrelated information. And that's a huge amount of data that is getting transferred. But if you're just scrolling through it, you could see how optimally they are using preloads and lazy loads to make sure that images kind of like everything kind of works in a sync and in a tandem. And that's a proper way of looking at application where like what will a user do next and how can you constrain that user to make sure that they're scrolling in one direction such that the images that you have are captured and downloaded even before the user goes to that picture. That's insane, insane amount of data that is getting downloaded. But on the other hand, the experience is much smoother and trying to think of image lazy loading and preloading kind of helps you over there and it's also like the threshold is what you're going to have and every time you're going to have image lazy loading there, it kind of goes ahead and saves much more data as well as much more better experience for your application. In line all the critical stuff that you have, the things that is essential for your application to run in line them and defer the non critical stuff. This is basically send the data that is required for your client and for users to see and remove anything else in your site that is not necessary for the users. Remove unused resources is another way of saying it. And if you can see, even though if you take most of the applications right away and just go ahead and use this coverage tool inside your Chrome, you could see around like 1 megabytes of data that is not unused in top sites and that is a huge number of data that is sent wasted. So you can also remove them out and try to ensure that you're not using them. So preload and lazy load will help you. And then finally you have this Chrome land, right, where everything live. And then you have the DNS where all your application traffic goes through or the traffic and the web server and then your application server, you have some folders or file systems where everything live and then you have the data where your database decides, right? So in this case, the first time when you load an HTML, it has to go to that long route and it has to reach your folder. It has to get that in the HTML and comes back and shows it there. You can use cache here to make sure that you're optimizing the performance. Once the HTML is loaded, it will be parsed, it will be completely parsed. And then it looks like, what are the files attached to it? And it takes those files. And this is what we call this critical resources. Right. So these are the critical resources that is essential for your application to run. Now, browser has to wait to finish the entire download of all the critical resources that you had. It over there. And once it fetches everything out, it goes and executes them. So you have to minimize the round trip that is going to happen. The critical resources that you're going to have, you're going to minimize the round trip. And finally, if you have some APIs, try to minimize the round trip that it goes and calls those API or end points that features data for you. So you have to minimize the API, road trips. And in the world of SSR, try to make sure that you're optimizing how many times your component calls the data and then gets it over there. Try to optimize it. So the round trips, try to minimize it always and use React profilers because they are very essential and it comes out of the package. You can use them to determine how your application is performing and what are the bottlenecks that you have in your application. You can go ahead and optimize the performance today and tomorrow it might have much less a performance or something like that. So it's an ongoing process. You have to think carefully what you're trying to give it to users. And it's an ongoing process that you have to deliver and always try to measure, optimize, find some problems, find some bottlenecks. Again, go ahead and correct those things and again, measure. So you have to measure, optimize, and measure things to make sure that your application is up to performance. Question. I am not sure whether we can do it. We just have 10 seconds, but thanks for you. Thank you so much, Sandy. That was a great talk and I hope everyone took some performance and optimization tips from this talk.