Skip to content

React 18: Understanding Automatic Batching

Posted on:February 3, 2026 at 07:27 PM

Hey code wizards! 🧙‍♂️

Remember the old days when you’d update two state variables in a row, and React would re-render your component twice? Yeah, that was annoying. It was like ordering a burger, the waiter running to the kitchen, then coming back to ask for your drink order, and running back again. Exhausting, right?

Well, React 18 introduced something called Automatic Batching, and it’s basically a smarter waiter.

Let’s dive in and see how this magic works.

The Problem: Too Many Re-renders

In React 17 and older, React only “batched” state updates if they happened inside a browser event (like a click). If you did it inside a setTimeout or a fetch callback, React would panic and re-render for every single state update.

Here is a classic example of the “bad old days”:

// React 17 behavior inside a timeout
setTimeout(() => {
  setCount(c => c + 1); // Re-render 1
  setFlag(f => !f);     // Re-render 2
}, 1000);

If you had a heavy component, this double rendering could make your UI feel sluggish. Not cool.

The Solution: Automatic Batching

React 18 is smarter. It says, “Hey, I see you’re updating multiple things. I’m gonna wait a split second, collect all your changes, and then update the DOM just once.”

It doesn’t matter where you trigger the updates:

Check this out:

import { useState } from 'react';

function BatchingExample() {
  const [count, setCount] = useState(0);
  const [flag, setFlag] = useState(false);

  function handleClick() {
    // React 18 will batch these automatically!
    // It doesn't matter if it's in a promise or timeout.
    setTimeout(() => {
      setCount(c => c + 1);
      setFlag(f => !f);
      // React will only re-render ONCE at the end of this!
    }, 1000);
  }

  console.log("Render!"); // This logs only once per click in React 18

  return (
    <div>
      <button onClick={handleClick}>Click Me</button>
      <h1 style={{ color: flag ? "blue" : "black" }}>{count}</h1>
    </div>
  );
}

This is a huge performance win out of the box. You don’t have to change your code; just upgrading to React 18 gives you this superpower.

Opting Out (If You Really Need To)

Sometimes, very rarely, you might want to force a re-render immediately after a state update. Maybe you need to read the DOM right away (though usually, a useEffect is better for that).

You can use flushSync from react-dom to tell React: “Do this NOW.”

import { flushSync } from 'react-dom';

function handleClick() {
  flushSync(() => {
    setCount(c => c + 1);
  });
  // React has updated the DOM here

  flushSync(() => {
    setFlag(f => !f);
  });
  // React has updated the DOM again here
}

But seriously, don’t use this unless you have a very specific reason. Let React do its thing.

Wrapping Up

Automatic Batching is one of those features that makes you love React even more. It’s fewer re-renders, better performance, and less code for us to worry about.

So, go ahead and update your states fearlessly! React has got your back.

Happy coding! 🚀