useId & useDebugValue
Stable ids for forms – and a label for the DevTools
useId produces a unique id that is identical on the server and in the browser. Meant for connecting a label to an input – not for list keys.
In HTML a label and its input belong together through an id: <label htmlFor="mail"> points at <input id="mail">. The snag: with two such fields on one page, clicking either label always lands in the first – the id appears twice.
A random number would be the obvious fix, but it breaks under Server rendering (SSR)The page is produced as finished HTML on the server, so the browser can show something straight away.→: the server and the browser roll different numbers, and React complains about a HydrationThe moment React takes over the HTML delivered by the server and brings it to life in the browser.If that HTML does not match what React would produce in the browser, you get a hydration error.→ mismatch. useId solves both.
function Field({ label }: { label: string }) {
const id = useId();
return (
<>
<label htmlFor={id}>{label}</label>
<input id={id} />
</>
);
}
// Deriving several ids from one call – this is the intended way:
function SignUp() {
const id = useId();
return (
<>
<label htmlFor={id + "-mail"}>Email</label>
<input id={id + "-mail"} />
<label htmlFor={id + "-pw"}>Password</label>
<input id={id + "-pw"} aria-describedby={id + "-hint"} />
<p id={id + "-hint"}>At least 12 characters</p>
</>
);
} - +
htmlFor/idbetween a label and a field - +
aria-describedby,aria-labelledby,aria-controls - +
Reusable components that may appear more than once on a page
- −
As a
keyin lists – for that you need an id from your data - −
As an id for records in a database
- −
As a replacement for
crypto.randomUUID()
useId gives one stable value per component instance – in a loop every element would get the same one. Keys have to come from your data.
▸ In more depth: useDebugValue optional
The second hook in this lesson is quickly told: useDebugValue writes a small label next to your own hook in the React DevTools – something like useOnline: "online". It has zero effect on how your app behaves.
function useOnline() {
const [online, setOnline] = useState(true);
useDebugValue(online ? "online" : "offline");
return online;
} Useful if you are writing a hook library. In ordinary application code you will not need it.
Does it stick?
2 questions on this lesson. Wrong answers show up in your stats.
Now write it yourself
The Field component is rendered twice. Right now both instances share one id – clicking the second label focuses the first field. Fix it with useId.
- □Each
<input>has its own unique id - □Each label's
htmlForpoints at its own field - □Clicking a label focuses exactly that input
import { useId } from "react"; function Field({ label }: { label: string }) { // TODO: create a unique id return ( <p> <label htmlFor="field">{label}</label>{" "} <input id="field" /> </p> ); } export default function App() { return ( <div style={{ fontFamily: "system-ui", padding: 16 }}> <Field label="First name" /> <Field label="Last name" /> </div> ); }