patternjavascriptreactTip
useId generates stable, unique IDs for accessibility attributes
Viewed 0 times
React 18+
useIdunique IDSSR hydrationaccessibilityariahtmlForhydration mismatch
Error Messages
Problem
Using Math.random() or a counter module to generate unique IDs for label/input associations (for/id, aria-labelledby, aria-describedby) causes hydration mismatches when the server-rendered ID doesn't match the client-rendered ID. It can also produce collisions when components render multiple times.
Solution
Use useId() to generate a stable, unique, SSR-safe ID:
import { useId } from 'react';
function PasswordInput() {
const id = useId(); // ':r0:' — stable on server and client
return (
<div>
<label htmlFor={id}>Password</label>
<input id={id} type="password" />
</div>
);
}
// Multiple IDs from one useId call
function FormGroup({ label, description }) {
const baseId = useId();
const labelId =
const descriptionId =
return (
<div>
<label id={labelId}>{label}</label>
<p id={descriptionId}>{description}</p>
<input
aria-labelledby={labelId}
aria-describedby={descriptionId}
/>
</div>
);
}
import { useId } from 'react';
function PasswordInput() {
const id = useId(); // ':r0:' — stable on server and client
return (
<div>
<label htmlFor={id}>Password</label>
<input id={id} type="password" />
</div>
);
}
// Multiple IDs from one useId call
function FormGroup({ label, description }) {
const baseId = useId();
const labelId =
${baseId}-label;const descriptionId =
${baseId}-description;return (
<div>
<label id={labelId}>{label}</label>
<p id={descriptionId}>{description}</p>
<input
aria-labelledby={labelId}
aria-describedby={descriptionId}
/>
</div>
);
}
Why
useId generates a deterministic ID based on the component's position in the tree. The same tree structure produces the same IDs on server and client, preventing hydration mismatches. IDs are globally unique across the component tree because React tracks position.
Gotchas
- useId IDs start with ':' — they are invalid as CSS selectors (can't use querySelector('#:r0:'))
- Don't use useId for list keys — keys must be data-driven, not position-based
- If you need multiple IDs, derive them from one useId call with string suffixes
- Available since React 18 — use a UUID library for older versions
Code Snippets
useId for label/input association
function Checkbox({ label }) {
const id = useId();
return (
<>
<input type="checkbox" id={id} />
<label htmlFor={id}>{label}</label>
</>
);
}Context
When generating unique IDs for HTML accessibility attributes in components that render on the server
Revisions (0)
No revisions yet.