useId Hook
useId Hook — creating unique IDs for SSR and Accessibility
Starting from React 18, a new hook was introduced — useId, which creates unique, stable, and predictable IDs. It's designed for cases where you need an ID that matches during server-side and client-side rendering.
This hook is especially useful in SSR projects (e.g., Next.js), where React first creates HTML on the server, then "rehydrates" it in the browser. If IDs differ on both sides, React will break the DOM connection.
Let's say we need to connect an input and label for accessibility purposes. For this, we need a unique ID for each input. However, in SSR projects, if the ID is generated with a random number (e.g., Math.random()), it will be different on the server and client, causing a warning:
Warning: Prop `id` did not match. Server: "input-17" Client: "input-83"
useId creates the same ID on both sides, ensuring stability and preventing hydration issues.
import React, { useId } from 'react';
export default function NameField() {
const id = useId();
return (
<div>
<label htmlFor={id}>Name</label>
<input id={id} type="text" placeholder="Write your name" />
</div>
);
}
Here React will automatically create an ID, for example: react-1:0,
and the same ID will be used in both server-side render and client-side hydration.
import React, { useId } from 'react';
export default function FormExample() {
const nameId = useId();
const emailId = useId();
return (
<form>
<div>
<label htmlFor={nameId}>Name</label>
<input id={nameId} type="text" />
</div>
<div>
<label htmlFor={emailId}>Email</label>
<input id={emailId} type="email" />
</div>
</form>
);
}
Each `useId()` call creates a stable, unique ID that React generates through its internal calculations. It ensures that when re-render or hydration occurs, the ID remains the same.
If you want to use one `useId()` in multiple places but with different postfixes, you can do it like this:
import React, { useId } from 'react';
export default function RangeSlider() {
const baseId = useId();
return (
<div>
<label htmlFor={`${baseId}-min`}>Minimum value</label>
<input id={`${baseId}-min`} type="range" />
<label htmlFor={`${baseId}-max`}>Maximum value</label>
<input id={`${baseId}-max`} type="range" />
</div>
);
}
This approach allows creating multiple sub-IDs from one base ID, which will also be stable in SSR.
react-1:2When using frameworks like Next.js that server-side render React, ID stability becomes crucial. useId ensures that the same ID is created on both server and client.
// pages/index.js
import { useId } from 'react';
export default function Home() {
const id = useId();
return (
<div>
<label htmlFor={id}>Username</label>
<input id={id} type="text" />
</div>
);
}
Without useId, the server and client would create different random IDs, causing React hydration error. With useId, they match 100%.
Write a component that: