diff --git a/content/5-tips-for-optimizing-your-react-apps-performance/debounced-search.gif b/content/5-tips-for-optimizing-your-react-apps-performance/debounced-search.gif
new file mode 100644
index 00000000..dfc979b5
Binary files /dev/null and b/content/5-tips-for-optimizing-your-react-apps-performance/debounced-search.gif differ
diff --git a/content/5-tips-for-optimizing-your-react-apps-performance/index.md b/content/5-tips-for-optimizing-your-react-apps-performance/index.md
index 9ae0bd6b..2fd75d1c 100644
--- a/content/5-tips-for-optimizing-your-react-apps-performance/index.md
+++ b/content/5-tips-for-optimizing-your-react-apps-performance/index.md
@@ -1,25 +1,31 @@
---
-title: 5 Tips for Optimizing Your React App’s Performance
+title: Tips for Optimizing Your React App’s Performance
date: "2024-07-01"
-description: "Poor app performance can reduce user engagement and will negatively affect SEO, here are 5 tips to optimize your react app"
+description: "Poor app performance can reduce user engagement and will negatively affect SEO, here are tips to optimize your react app"
cover: "optimize-react-app-performance.png"
category: "featured"
author: "Connor Peshek"
---
-## Table of contents
## Table of Contents
-1. [Introduction](#introduction)
-2. [Understanding React Performance](#understanding-react-performance)
-3. [Performance Optimization Techniques](#performance-optimization-techniques)
-4. [Avoid Anonymous Functions in JSX](#avoid-anonymous-functions-in-jsx)
-5. [Avoid Excessive Component Re-renders](#avoid-excessive-component-re-renders)
-6. [Use Production Build](#use-production-build)
-7. [Conclusion](#conclusion)
-
+## Table of Contents
-# Importance of Performance Optimization in React Apps
+- [Introduction](#introduction)
+- [How does React’s Virtual DOM work?](#how-does-reacts-virtual-dom-work)
+- [React Performance Optimization Techniques](#11-react-performance-optimization-techniques)
+ - [Measure React Performance](#measure-react-performance)
+ - [Code Splitting and Lazy Loading](#code-splitting-and-lazy-loading)
+ - [Implement React Server-Side Rendering (SSR)](#implement-react-server-side-rendering-ssr)
+ - [Virtualize Lists](#virtualize-lists)
+ - [Properly Use React's Keys Property When Rendering a List](#properly-use-reacts-keys-property-when-rendering-a-list)
+ - [Memoization](#memoization)
+ - [useMemo](#usememo)
+ - [Implement Throttling and Debouncing](#implement-throttling-and-debouncing)
+ - [useTransition Hook](#usetransition-hook)
+- [Conclusion](#conclusion)
+
+## Introduction
Optimizing performance in a React app isn’t just about being a good developer, it's important because less optimized pages make less money and rank lower on Google SEO. The internet is competitive. There are thousands of websites competing for everyone’s limited attention spans, and people hate to wait. People hate waiting so much that a report found that a site that loads in 1 second has a conversion rate 3x higher than a site that loads in 5 seconds. That’s a big difference. And around 2020, Google started using the core web vital metrics to factor in your Google ranking from a search result, meaning that less optimized sites rank lower on Google searches.
@@ -76,6 +82,8 @@ ReactDOM.render(
You can also use the React Developer Tools in your web browser by installing the plugin from your browser’s extension marketplace - such as the Chrome Web Store - and opening your browser's developer tools. Two React tabs will be added to your developer console - components and profiler. The components tab can tell you details like props of rendered React components, and the profiler tab will allow you to record a rendering timeline of your React application. Simply click the record button and trigger a render, or click the refresh button to record all rendering times from a new page refresh. This can help you get a baseline of what components are rendering quickly and which ones could be causing performance bottlenecks. You can also check things like why a component is re-rendering and use some of the tips later in the article to correct it.
+![measure react performace](./measure-react-performace.gif)
+
### Code splitting and lazy loading
Code splitting allows you to break your front-end react bundle into smaller chunks that can be downloaded together in parallel or at the time they are needed, and lazy loading allows you to load less important parts of an application later, improving application performance. These combined can help increase initial load times for an application, affecting our LCP metric.
@@ -131,14 +139,57 @@ List virtualization, also known as windowing, is when you only render the part o
Let’s say you have an e-commerce store and someone does a search for "The". I don't know why they would do that, but it's the most common word in the English language, meaning you’ll probably have to render a pretty long list of results. If we virtualize the list instead of rendering all thousand entries, we can cut down on the render time and prevent freezing while scrolling.
-There are many libraries, such as react-virtualized, react-window (an updated, lightweight alternative to react-virtualized made by the same team), and react-virtuoso, that can make implementing virtualized lists simple.
+There are many libraries, such as [react-virtualized](https://github.com/bvaughn/react-virtualized), [react-window (an updated, lightweight alternative to react-virtualized made by the same team)](https://github.com/bvaughn/react-window), and [react-virtuoso](https://github.com/petyosi/react-virtuoso), that can make implementing virtualized lists simple. Let's use react-virtuoso to virtualize a list of 100 pokemon.
+
+```js
+import React, { useEffect, useState } from 'react';
+import { Virtuoso } from 'react-virtuoso';
+import ReactDOM from 'react-dom/client';
+
+const App = () => {
+ const [pokemon, setPokemon] = useState([]);
+
+ useEffect(() => {
+ async function fetchPokemon() {
+ const response = await fetch('https://pokeapi.co/api/v2/pokemon?limit=151');
+ const data = await response.json();
+ setPokemon(data.results);
+ };
+ fetchPokemon();
+ }, []);
+
+ return (
+
+
Virtualized Pokémon List
+ {
+ const poke = pokemon[index];
+ return (
+
+ {index + 1}. {poke.name}
+
+ );
+ }}
+ />
+
+ );
+};
+
+const root = ReactDOM.createRoot(document.getElementById('root'));
+root.render();
+```
+Look how smooth that scroll is.
+
+![virtualized lists](./virtualized-lists.gif)
### Properly use React's Keys property when rendering a list
React requires giving every item in a generated list of jsx a unique key. People tend to use the array index to handle this, but this can cause unnecessary re-renders. Keys are used by react to tell if it needs to re-render items in the list. If you use the array index and then an item is removed or the array is sorted, the keys in the new list will no longer match the old index keys, and all of those items will have to be re-rendered. If we use unique keys, like a userId, we can prevent React from needing to re-render any items in the list when the list’s order changes.
-Good idea
+#### Good Idea
```js
// index.js
@@ -172,4 +223,422 @@ const App = () => {
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render();
-```
\ No newline at end of file
+```
+
+#### Bad Idea
+
+```js
+import ReactDOM from 'react-dom/client';
+import React from 'react';
+
+const App = () => {
+ const users = [
+ { userId: 1, name: 'Alice', email: 'alice@example.com' },
+ { userId: 2, name: 'Bob', email: 'bob@example.com' },
+ { userId: 3, name: 'Charlie', email: 'charlie@example.com' },
+ ];
+
+
+ return (
+
+
User List
+
+ {users.map((user, index) => (
+
+
{user.name}
+
{user.email}
+
+ ))}
+
+
+
+ );
+ };
+
+const root = ReactDOM.createRoot(document.getElementById('root'));
+root.render();
+```
+
+### Memoization
+
+Memoization is a specific type of caching where you cache the result of a function. This prevents you from calculating the same values over and over again.
+
+In React, every time a component's state changes, it causes all of its children to re-render, regardless if the children have had state or prop changes. This prevents UI errors, but at the expense of performance for your app. However, with memoization, we can cache functional components, calculations they have to make, or functions they use in order to skip fully re-rendering components.
+
+React has the useMemo hook, which caches the results of a function call; React.memo, a higher order component which can render a functional component from cache if its props are unchanged (since functional components are, well, functions); and useCallback, which caches a function declaration.
+
+#### useMemo
+
+useMemo caches the result of a function. This is especially useful if you have a component that has large calculations it has to handle. For a practical example, if you had an e-commerce store that had a large list of items, and you needed to calculate tax cost for each item based on location in the front-end. You may need to loop through a large list of items every time a parent component has a state change, even if nothing in its own state or props hasn't changed. But for the sake of having a bundled example showing re-rendering and the speed of rendering from cache, our example code will go through a massive loop and add to a number instead of adding tax to a list of items. By memoizing the result of our expensiveCalculation function, even when our parent components state updates, we will reuse the last result of the function as long as the count hasn't changed.
+
+
+```js
+// index.js
+import ReactDOM from 'react-dom/client';
+import React, { useState, useMemo } from 'react';
+
+const App = () => {
+ const [count, setCount] = useState(0);
+ const [input, setInput] = useState('');
+
+ // Expensive calculation function
+const expensiveCalculation = (num) => {
+ console.log('Calculating...');
+ let result = 0;
+ for (let i = 0; i < 1000000000; i++) {
+ result += num;
+ }
+ return result;
+};
+
+ const memoizedValue = useMemo(() => expensiveCalculation(count), [count]);
+
+ return (
+
+ );
+};
+
+const root = ReactDOM.createRoot(document.getElementById('root'));
+root.render();
+
+```
+
+**Without `useMemo`**
+
+![without useMemo](./without-use-memo.gif)
+
+**With `useMemo`**
+
+![with useMemo](./with-use-memo.gif)
+
+
+### Implement Throttling and debouncing
+
+Throttling is when you call a function on an event and then delay the continual execution of the function to certain time intervals as the event continues to trigger, and debouncing is when you wait a certain amount of time after an event happens to execute a function. They can both help with your INP score by making user interactions smoother. They sound very similar, but there is a slight difference between the two, and we can see it in how and why they’re implemented. We will implement both throttling and debouncing with the lodash library.
+
+Let's pretend we were a mega sized tech company that had an application to allow writing documents and saving the data in the cloud. We'll call the application Doodle Docs. We wouldn't want to save the document after every keystroke, because that would be overkill. But we also wouldn't want to wait until after they stopped typing, because someone could be on a huge typing spree, lose internet connection, and then lose all their work. That would be rough! So what if instead of waiting for them to stop, we just periodically backed up what they were typing in time intervals? That's exactly how throttling would work in this example.
+
+
+```js
+// index.js
+import ReactDOM from 'react-dom/client';
+import React, { useState, useCallback } from 'react';
+
+// be sure to npm install --save lodash for this to work
+import _ from 'lodash';
+
+function App() {
+ const [text, setText] = useState('');
+
+ const throttledLog = useCallback(
+ _.throttle((value) => {
+ // Doodle docs would PUT request the data somewhere
+ // instead of logging to console
+ console.log(value);
+ }, 500),
+ []
+ );
+
+ const handleChange = (event) => {
+ setText(event.target.value);
+ throttledLog(event.target.value);
+ };
+
+ return (
+
+
Throttled Textbox
+
+
+ );
+}
+
+const root = ReactDOM.createRoot(document.getElementById('root'));
+root.render();
+```
+
+![throttled textbox](./throttled-textbox.gif)
+
+Debouncing is useful for things like limiting search or filter results. Instead of filtering search results of pokemon on every keystroke (which is 5 characters per second for a 60 wpm typer), you can set your event listener to wait a second after the last key is pressed to display results. This makes the user experience smoother than flickering a bunch of results 5 times per second and can save you from making unneeded re-renders.
+
+```js
+// index.js
+import ReactDOM from 'react-dom/client';
+import React, { useState, useTransition, useEffect } from 'react';
+// make sure to npm install --save lodash for this to work
+import _ from 'lodash';
+
+const Counter = () => {
+ const [count, setCount] = useState(0);
+
+ return (
+ <>
+
+ );
+};
+
+const root = ReactDOM.createRoot(document.getElementById('root'));
+root.render();
+
+```
+
+![Debounced Search](./debounced-search.gif)
+
+### useTransition hook
+
+The [useTransition hook](https://react.dev/reference/react/useTransition) allows you to update the state of your application without blocking the rest of the User Interface. Filtering through and updating large amounts of state can cause performance issues and lock up your application, but useTransition can fix this and help improve your INP metric.
+
+You may be thinking to yourself "Gee, this sounds pretty similar to debouncing and throttling". And yeah, it's in the same ballpark. But useTransition is better suited for UI transitions involved with heavy computation. Since it can handle the UI changes as a lower priority than the rest of the UI, users can still interact with the app - as where throttling or debouncing might smooth out how often we are computing, but can still freeze if the computations are too heavy.
+
+Let’s say we have a large list of pokemon and we want to search through them. This will probably run fine on a 2024 macbook, but what about on a 10 year old phone? We can bet it won't run as smoothly on older hardware like that, and sometimes that's what our users are using to interact with our web apps. We'll simulate using older hardware by opening the chrome dev tools, going to the performance tab, and setting the cpu throttling to x20 when we run our app. Instead of the entire web app locking up and being unresponsive while we sort and update the pokemon list, we can use useTransition, set a loading screen inside the component, and update the state of the component while still allowing full use of the rest of the UI.
+
+```js
+// src/index.js
+import ReactDOM from 'react-dom/client';
+import React, { useState, useTransition, useEffect } from 'react';
+
+const Counter = () => {
+ const [count, setCount] = useState(0);
+
+ return (
+ <>
+
+ );
+};
+
+const root = ReactDOM.createRoot(document.getElementById('root'));
+root.render();
+```
+
+![search with useTransition](./pokemon-search-transition.gif)
+
+
+### Inline arrow functions and CSS
+
+If you use inline arrow functions as props, React will re-create the function every time it’s re-rendered, wasting compute time. Also, using inline arrow functions and styles can undo memoization. Inline styles in react also have to be converted from a javascript object to css styling, which wastes compute time.
+
+Instead of using inline arrow functions and css, define your event handlers inside the component whenever possible and import your CSS from an external style sheet.
+
+#### Good Idea
+
+```js
+import ReactDOM from 'react-dom/client';
+import React from 'react';
+
+const App = () => {
+ const handleClick = () => {
+ console.log('clicked!');
+ };
+
+ return (
+
+
+
+ );
+};
+
+
+const root = ReactDOM.createRoot(document.getElementById('root'));
+root.render();
+```
+
+#### Bad Idea
+
+```js
+// src/index.js
+import ReactDOM from 'react-dom/client';
+import React from 'react';
+
+const App = () => {
+ return (
+
+
+
+ );
+};
+
+const root = ReactDOM.createRoot(document.getElementById('root'));
+root.render();
+```
+
+### React fragments
+
+[React fragments](https://react.dev/reference/react/Fragment) allow you to render a react component without making a wrapper node. Using this throughout a large app can remove a lot of unnecessary dom nodes from the web page and make rendering the DOM faster.
+
+```js
+//src/ index.js
+import ReactDOM from 'react-dom/client';
+import React from 'react';
+
+const App = () => {
+
+ return (
+ <>
+
I'm an h1 inside a React Fragment!
+ >
+ );
+};
+
+const root = ReactDOM.createRoot(document.getElementById('root'));
+root.render();
+```
+
+## Conclusion
+
+After implementing all these react performance techniques, the speed, responsiveness, and user experience of your application will be noticeably improved. Now that your optimized site will be ranking on the top of google and keeping customers engaged, why not try adding rock solid authentication with Supertokens!
diff --git a/content/5-tips-for-optimizing-your-react-apps-performance/measure-react-performace.gif b/content/5-tips-for-optimizing-your-react-apps-performance/measure-react-performace.gif
new file mode 100644
index 00000000..e2fc195a
Binary files /dev/null and b/content/5-tips-for-optimizing-your-react-apps-performance/measure-react-performace.gif differ
diff --git a/content/5-tips-for-optimizing-your-react-apps-performance/pokemon-search-transition.gif b/content/5-tips-for-optimizing-your-react-apps-performance/pokemon-search-transition.gif
new file mode 100644
index 00000000..f959cedf
Binary files /dev/null and b/content/5-tips-for-optimizing-your-react-apps-performance/pokemon-search-transition.gif differ
diff --git a/content/5-tips-for-optimizing-your-react-apps-performance/throttled-textbox.gif b/content/5-tips-for-optimizing-your-react-apps-performance/throttled-textbox.gif
new file mode 100644
index 00000000..b202a309
Binary files /dev/null and b/content/5-tips-for-optimizing-your-react-apps-performance/throttled-textbox.gif differ
diff --git a/content/5-tips-for-optimizing-your-react-apps-performance/virtualized-lists.gif b/content/5-tips-for-optimizing-your-react-apps-performance/virtualized-lists.gif
new file mode 100644
index 00000000..0b90e304
Binary files /dev/null and b/content/5-tips-for-optimizing-your-react-apps-performance/virtualized-lists.gif differ
diff --git a/content/5-tips-for-optimizing-your-react-apps-performance/with-use-memo.gif b/content/5-tips-for-optimizing-your-react-apps-performance/with-use-memo.gif
new file mode 100644
index 00000000..ad62e30c
Binary files /dev/null and b/content/5-tips-for-optimizing-your-react-apps-performance/with-use-memo.gif differ
diff --git a/content/5-tips-for-optimizing-your-react-apps-performance/without-use-memo.gif b/content/5-tips-for-optimizing-your-react-apps-performance/without-use-memo.gif
new file mode 100644
index 00000000..818e9a36
Binary files /dev/null and b/content/5-tips-for-optimizing-your-react-apps-performance/without-use-memo.gif differ
diff --git a/static/blog-seo/config.json b/static/blog-seo/config.json
index f5455969..983f4e1b 100644
--- a/static/blog-seo/config.json
+++ b/static/blog-seo/config.json
@@ -143,26 +143,26 @@
{
"path": "/blog/5-tips-for-optimizing-your-react-apps-performance",
"metaTags": [
- " ",
- "",
+ " ",
+ "",
"",
"",
"",
- "",
+ "",
"",
"",
- "",
+ "",
"",
"",
"",
- "",
- "",
+ "",
+ "",
"",
" ",
""
],
- "title": "5 Tips for Optimizing Your React App’s Performance",
- "schema": ""
+ "title": "Tips for Optimizing Your React App’s Performance",
+ "schema": ""
},
{
"path": "/blog/creating-great-authentication-experiences-with-custom-ui",