React useId — Generating Unique IDs for Accessible Forms
Learn how to use useId to generate stable, unique IDs for form elements and ARIA attributes. Covers htmlFor, aria-describedby, and avoiding hydration mismatches in SSR applications.
Detailed Explanation
Accessible IDs with useId
useId generates a unique ID that is stable across server and client renders. It solves the problem of creating accessible form labels without hydration mismatches.
The Problem
Before useId, developers used incrementing counters or Math.random() for IDs. Both approaches fail with server-side rendering because the server and client generate different values, causing hydration mismatches.
Basic Usage
function PasswordField() {
const id = useId();
return (
<div>
<label htmlFor={id}>Password</label>
<input id={id} type="password" />
</div>
);
}
Deriving Multiple IDs
Call useId once and derive related IDs with suffixes:
function FormField({ label, hint }: { label: string; hint: string }) {
const id = useId();
const inputId = id + "-input";
const hintId = id + "-hint";
const errorId = id + "-error";
return (
<div>
<label htmlFor={inputId}>{label}</label>
<input
id={inputId}
aria-describedby={hintId}
aria-errormessage={errorId}
/>
<p id={hintId}>{hint}</p>
</div>
);
}
What NOT to Use useId For
- List keys: Use your data's natural key (database ID, unique name), not useId
- CSS selectors: The generated format (:r0:) is not suitable for CSS
- Custom formatting: You cannot control the ID format
SSR Compatibility
useId works seamlessly with Next.js, Remix, and other SSR frameworks. The generated IDs match between server and client, preventing the "text content does not match" hydration errors that plague other ID generation approaches.
Use Case
Use useId for accessible form elements with htmlFor/id pairs, ARIA attribute connections (aria-describedby, aria-labelledby, aria-errormessage), and any scenario requiring unique IDs that are stable across server and client rendering.