Hello friends! 👋
Have you ever opened your browser console and seen a scary red warning like this?
Warning: Prop "id" did not match. Server: "cl-123" Client: "cl-456"
That is called a Hydration Mismatch.
It usually happens when you try to generate random IDs for your components using something like Math.random().
Why? Because the server generates one random number, and your browser generates a different random number. React gets confused because the trees don’t match. 🤯
The Old Way (The Bad Way) ❌
In the past, we had to jump through hoops to fix this. We used global counters or external libraries just to connect a <label> to an <input>.
If you did this, you were asking for trouble:
// Don't do this!
const BadComponent = () => {
const id = `input-${Math.random()}`; // 😱 Unstable ID
return (
<>
<label htmlFor={id}>Name</label>
<input id={id} type="text" />
</>
);
};
The Solution: useId hook ✅
React 18 introduced a new hook specifically for this problem: useId.
It generates a unique string that is stable across both the server and the client. No more hydration errors!
Here is how you use it:
import { useId } from 'react';
const GoodComponent = () => {
const id = useId();
return (
<div>
<label htmlFor={id}>Email Address</label>
<input id={id} type="email" />
</div>
);
};
Simple, right?
What if I need multiple IDs?
You don’t need to call useId ten times for one form. You can just append a suffix to the base ID.
import { useId } from 'react';
const RegistrationForm = () => {
const baseId = useId();
return (
<form>
<div>
<label htmlFor={`${baseId}-name`}>Name</label>
<input id={`${baseId}-name`} type="text" />
</div>
<div>
<label htmlFor={`${baseId}-email`}>Email</label>
<input id={`${baseId}-email`} type="email" />
</div>
</form>
);
};
This keeps everything connected and accessible. ♿️
A Big Warning ⚠️
Do NOT use useId to generate keys for a list.
// ❌ NEVER DO THIS
<ul>
{items.map(item => (
<li key={useId()}>{item.name}</li> // This is bad!
))}
</ul>
Keys should be generated from your data (like item.id), not from the component instance. useId is for accessibility attributes, not data reconciliation.
Conclusion
The useId hook is a small but mighty addition to React 18. It solves the hydration mismatch problem once and for all and makes building accessible forms much easier.
If you are still using random number generators for IDs, it is time to upgrade!
Happy Coding! 🚀