Next.js server prop did not match client prop; And conditional style/class rendering

gvegacl

Cesar Vega

Posted on September 11, 2023

Next.js server prop did not match client prop; And conditional style/class rendering

Have you ever gotten the enigmatic Next.js error: "Warning: Prop "{your-prop}" did not match. Server: "{value-1}" Client: "{value-2}"?

Here is the fun fact, conditional rendering did not cause this error. But at the beginning I thought it did, that's why I created this post, so everyone struggling to get rid of that error can understand why it happens, and how to solve it.

Here is our example:

type TGrid = (0 | 1)[][];

const generateRandomTiles = () => {
  const grid: TGrid = [];
  for (let i = 0; i < numRows; i++) {
    grid.push(Array.from(Array(numCols), () => (Math.random() > 0.7 ? 1 : 0))); // returns a live cell 70% of the time
  }
  return grid;
};

export const Game = () => {
  const [grid, setGrid] = useState<TGrid>(generateRandomTiles());

  return (
    grid?.map((rows, i) =>
      rows.map((col, k) => {
          return (
            <div
              // classNames is a library that combines class texts. But this can be thought as "w-4 h-4 border-solid border border-gray-400" + grid[i][k] ? "bg-pink-400" : "bg-white"
              className={classNames(
              "w-4 h-4 border-solid border border-gray-400",
              grid[i][k] ? "bg-pink-400" : "bg-white"
              )}
              key={`${i}-${k}`}
            />
          )
      })
    )
  );
};

Enter fullscreen mode Exit fullscreen mode

This code causes the following error to fire:

Warning: Prop className did not match. Server: "w-4 h-4 border-solid border border-gray-400 bg-white" Client: "w-4 h-4 border-solid border border-gray-400 bg-pink-400"

So I thought it happened because I conditionally rendered bg-pink-400 and bg-white. But it wasn't, the real problem is a little more sneaky than that.

Do not generate values when defining useState.

The real culprit is this the line where we define our grid state:
const [grid, setGrid] = useState<TGrid>(generateRandomTiles());

The problem here is that Next.js has certain state in mind when we generate the tiles in the server, but when the client runs hydrates, we end up with a different random array when we generateRandomTiles() in the frontend. You can notice this if you console log grid, and check your next.js terminal log and your frontend log.

There is a great article by react.gg about this: https://ui.dev/c/react/effects?ck_subscriber_id=2248853627

How do we fix it?

Simple, we manage effects on the frontend. Like this:


const [grid, setGrid] = useState<TGrid>();

  useEffect(() => {
    const initialGrid = generateRandomTiles();
    // Comment, first refresh
    setGrid(initialGrid);
  }, []);

Enter fullscreen mode Exit fullscreen mode

Now we will not have a mismatch between frontend and backend style rendering, and the error will not happen anymore.

Useful links if you want to go deeper:

💖 💪 🙅 🚩
gvegacl
Cesar Vega

Posted on September 11, 2023

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related