useId
is a React Hook for generating unique IDs that can be passed to accessibility attributes.
Following is the signature of useId
hook:
const id = useId()
It takes no argument and returns a unique id associated with that particular useId
call and in that particular component. It shouldn’t be used to generate unique ids for other purpose (other than accessibility attributes).
useId
can be used both on server and client.
With server rendering, useId
requires an identical component tree on the server and the client. If the trees we render on the server and the client don’t match exactly, the generated IDs won’t match.
IDs generated by useId
shouldn’t and can’t be used as CSS selectors (because of their pattern of id that’s generated).
Following in an example of using useId
:
import { useId } from "react";
export default function App(): JSX.Element {
const usernameHintId = useId();
return (
<div>
{/* With useId */}
<input type="text" aria-describedby={usernameHintId} />
<p id={usernameHintId}>Enter your username</p>
{/* Without useId */}
<label>
Password:
<input type="password" aria-describedby="password-hint" />
</label>
<p id="password-hint">
The password should contain at least 18 characters
</p>
</div>
);
}
Why
useId
is better than incrementing a global variable likenextId++
?The primary benefit of
useId
is that React ensures that it works with server rendering. During server rendering, our components generate HTML output. Later, on the client, hydration attaches our event handlers to the generated HTML. For hydration to work, the client output must match the server HTML.The primary benefit of
useId
is that React ensures that it works with server rendering. This is very difficult to guarantee with an incrementing counter because the order in which the Client Components are hydrated may not match the order in which the server HTML was emitted. By callinguseId
, we ensure that hydration will work, and the output will match between the server and the client.Inside React,
useId
is generated from the “parent path” of the calling component. This is why, if the client and the server tree are the same, the “parent path” will match up regardless of rendering order.