useId generates unique IDs that are consistent between server and client rendering. This is critical for SSR/SSG applications where IDs must match to avoid hydration mismatches.
Basic usage:
1import { useId } from "react";23function LoginForm() {4 const emailId = useId();5 const passwordId = useId();6 const errorId = useId();78 return (9 <form>10 <div>11 <label htmlFor={emailId}>Email</label>12 <input id={emailId} type="email" aria-describedby={errorId} />13 </div>14 <div>15 <label htmlFor={passwordId}>Password</label>16 <input id={passwordId} type="password" />17 </div>18 <span id={errorId} role="alert" />19 <button type="submit">Login</button>20 </form>21 );22}
Why not use Math.random()?
Why not use index or static IDs?
How useId works:
:r1:, :r2:, :r3: (prefixed with colons)Use cases:
htmlFor/idaria-describedby, aria-labelledby, aria-controlsAdvanced example with ARIA:
1function SearchBox() {2 const inputId = useId();3 const descriptionId = useId();4 const resultsId = useId();56 return (7 <div>8 <label htmlFor={inputId}>Search products</label>9 <input10 id={inputId}11 type="search"12 aria-describedby={descriptionId}13 aria-controls={resultsId}14 aria-autocomplete="list"15 />16 <span id={descriptionId}>17 Type to search across all products18 </span>19 <div id={resultsId} role="listbox">20 {/* Search results here */}21 </div>22 </div>23 );24}
Note: useId is not for list keys — use a stable ID from your data (like item.id) for that.