Faiz Khan | Portfolio | Fullstack Developer

Faiz Khan | Next.js | Freelancer | Web Developer | Fullstack Developer | Portfolio | Web Development | Portfolio Website | Blog | Software Development | Personal Website | Software Engineer | React | Typescript | Node.js | Python | MongoDB | TailwindCSS | Express.js

Faiz Khan | Portfolio

Explore the portfolio of Faiz Khan, showcasing my skills, projects, and experience as a fullstack developer

Next.js | React | Typescript | Node.js | TailwindCSS | Python | AWS | GCP | Docker | AI

React 18

Optimizing React Performance: Leveraging React 18 Features with Bun

Optimizing React Performance: Leveraging React 18 Features with Bun
0 views
4 min read
#React 18

Introduction

React 18 has brought exciting new features to improve rendering performance, such as concurrent rendering, useTransition, useDeferredValue, and more. But to take performance optimization to the next level, you can pair React 18 with Bun, a super-fast runtime that speeds up both development and production builds. In this blog, we'll guide you through optimizing React 18 applications using its latest performance-enhancing features while running your project with Bun.

Step 1: Setting Up React 18 with Bun

First, create a new React 18 project using Bun as your runtime. Bun provides a faster package manager and runtime, making it ideal for React projects that demand performance.

bun create react-app@latest my-optimized-app
cd my-optimized-app

Now, you'll have a fresh React 18 setup. Let's install any additional dependencies like React Router or state management libraries if needed.

bun add react-router-dom

Step 2: Understanding React 18's Concurrent Rendering

One of the most powerful additions in React 18 is Concurrent Rendering, allowing React to interrupt rendering tasks to keep the UI responsive. This is key for performance on slower devices or when performing complex updates.

You don’t need to change how you write React components for concurrent rendering—it happens under the hood. However, React 18 enables concurrent rendering only when you use features like useTransition.

Step 3: Optimizing with useTransition

The useTransition hook allows you to mark updates as non-urgent, letting React prioritize more critical updates (like typing into an input field). Here’s an example where we use useTransition to prioritize input changes over expensive state updates:

import { useState, useTransition } from 'react';

export default function SearchComponent() {
  const [query, setQuery] = useState('');
  const [results, setResults] = useState([]);
  const [isPending, startTransition] = useTransition();

  const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    const value = e.target.value;
    setQuery(value);

    startTransition(() => {
      // Simulate expensive task like searching a large dataset
      setResults(doExpensiveSearch(value));
    });
  };

  return (
    <div>
      <input
        type="text"
        value={query}
        onChange={handleInputChange}
        placeholder="Search..."
      />
      {isPending ? <p>Loading...</p> : <ul>{results.map(renderResult)}</ul>}
    </div>
  );
}

In this example, startTransition marks the search update as a non-urgent task, allowing the input to remain responsive even when the search results are being updated.

Step 4: Using useDeferredValue for Smooth Rendering

The useDeferredValue hook works similarly to useTransition, but is used for individual values. It defers rendering non-critical updates to reduce lag in your app. Let’s take a look:

import { useState, useDeferredValue } from 'react';

export default function DeferredComponent() {
  const [inputValue, setInputValue] = useState('');
  const deferredValue = useDeferredValue(inputValue);

  return (
    <div>
      <input
        type="text"
        value={inputValue}
        onChange={(e) => setInputValue(e.target.value)}
      />
      <ExpensiveComponent value={deferredValue} />
    </div>
  );
}

Here, useDeferredValue will delay rendering the ExpensiveComponent until the user has stopped typing, leading to smoother UI interactions.

Step 5: Combining Bun with React 18 for Faster Builds

Bun not only accelerates development by providing a faster runtime, but it also optimizes production builds. By default, Bun builds are already optimized for performance. You can trigger a production build using:

bun build

This will bundle your app for production, applying various performance enhancements, including minification and dead code elimination, making your React 18 app load faster.

Step 6: Additional Performance Tips

  1. Use React.memo: This helps in memoizing components to avoid unnecessary re-renders.

    const MemoizedComponent = React.memo(function MyComponent() {
      // component code
    });
  2. Lazy Loading: Load components only when they are needed using React’s lazy and Suspense.

    const LazyComponent = React.lazy(() => import('./LazyComponent'));
    
    function App() {
      return (
        <Suspense fallback={<div>Loading...</div>}>
          <LazyComponent />
        </Suspense>
      );
    }
  3. Avoid Anonymous Functions: Passing anonymous functions directly to event handlers can cause unnecessary re-renders. Instead, define functions outside the render method or memoize them using useCallback.

Conclusion

By using React 18’s powerful concurrent features like useTransition and useDeferredValue, combined with the blazing-fast runtime provided by Bun, you can create a high-performance React app that's both responsive and quick to load. Implementing these optimizations will ensure your web applications remain fast and efficient even as they scale.

Happy coding with React 18 and Bun!