useId devuelve un id único para cada instancia de un componente, y el mismo id en cada renderizado. Úsalo para conectar un <label> con su <input>, o un input con su pista mediante aria-describedby, cuando el componente puede aparecer más de una vez en una página.
El mismo componente se renderiza dos veces y recibe dos ids distintos, impresos bajo cada campo. Haz clic en el texto "Confirm password" y el foco salta al segundo input, porque su htmlFor coincide solo con el id de ese input. Escribe a mano id="password" en lugar de useId() y ambos labels apuntan al primer input.
La sintaxis
const id = useId();
useId no recibe argumentos y devuelve una cadena. Llámalo en el nivel superior del componente, como cualquier hook. Su formato exacto es interno y ha cambiado entre versiones: React 18 producía :r1:, y React 19.2 produce _r_1_ para un componente renderizado primero en el navegador y un id que empieza con _R_, construido a partir de la posición en el árbol, para uno renderizado en el servidor. Nunca lo analices ni dependas de su forma.
Por qué no Math.random ni un contador
Los ids para accesibilidad tienen que coincidir entre el HTML que envía el servidor y el árbol que construye React en el navegador. Las dos formas obvias de crear un id fallan esa prueba.
// Changes on every render, and differs between server and browser
const id = 'field-' + Math.random().toString(36).slice(2);
// The server's counter keeps growing across requests, the browser starts at 0
let nextId = 0;
const id = 'field-' + nextId++;
Con renderizado en el servidor (Next.js, React Router en modo framework, cualquier configuración con hydrateRoot), el servidor imprime id="field-4817" en el HTML, el primer renderizado del navegador calcula field-0, y React reporta un desajuste de hidratación. useId construye el id a partir de la posición del componente en el árbol, que es idéntica en ambos lados.
Incluso sin servidor, un id construido durante el renderizado puede cambiar en cada renderizado. Este ejemplo muestra la diferencia sin ningún servidor de por medio:
Haz clic en el botón varias veces. El valor de useId se queda quieto mientras el id del contador sube con cada renderizado, así que todo lo que apuntaba al id viejo (un aria-describedby, un label) ahora no apunta a nada. Envolver el contador en useState(() => nextId++) arreglaría los nuevos renderizados pero no el desajuste con el servidor.
Varios ids de una sola llamada
Un componente con varios campos no necesita varias llamadas a useId. Genera una base y agrega un sufijo por elemento.
Escribe una palabra sin @ en el campo del email: aparece el error y el aria-describedby del input apunta a él, así que un lector de pantalla lee el error cuando el input tiene el foco. Renderiza <SignupForm /> dos veces en App y cada copia recibe su propia base de id.
No sirve para las keys de una lista
Las keys y los ids resuelven problemas distintos. Una key le dice a React qué elemento es cuál entre renderizados, así que debe venir de los datos. useId da un id por instancia de componente, y no puedes llamarlo dentro de map.
// Wrong: breaks the rules of hooks, and the key is unrelated to the item
{todos.map((todo) => <Todo key={useId()} todo={todo} />)}
// Right: the key comes from the data
{todos.map((todo) => <Todo key={todo.id} todo={todo} />)}
Cuando tus datos no tienen un id, créalo cuando se crea el elemento (crypto.randomUUID() en el manejador de eventos que lo agrega), no durante el renderizado. La página de listas y keys explica por qué la key debe quedarse con su elemento.
Varias raíces de React en una página
Si dos apps de React separadas renderizan en la misma página, sus ids podrían chocar. Dale un prefijo a cada raíz:
createRoot(document.getElementById('cart'), { identifierPrefix: 'cart-' });
createRoot(document.getElementById('chat'), { identifierPrefix: 'chat-' });
Con renderizado en el servidor, pasa el mismo identifierPrefix al renderizador del servidor y a hydrateRoot para que ambos lados produzcan los mismos ids.
Errores comunes
Renderizar un árbol distinto en el servidor y en el navegador. useId depende de la posición del componente, así que una rama como typeof window === 'undefined' ? <A /> : <B /> por encima de un campo puede desplazar los ids entre los dos renderizados. Mantén el árbol igual durante la hidratación y cambia después, en un efecto.
Buscar el elemento por su id. document.getElementById(id) funciona, pero una ref es la forma de React de llegar a un nodo del DOM y no necesita ningún id.
Usarlo como un valor aleatorio. El id es único dentro de la app, no aleatorio, y se puede predecir a partir del árbol. No lo uses para tokens de seguridad, claves de caché ni nada que se guarde entre sesiones.
Cuándo usarlo
Usa useId siempre que un componente reutilizable necesite un atributo id: campos de formulario construidos a partir de un componente que se usa muchas veces, un tooltip conectado con aria-describedby, un diálogo con aria-labelledby, pestañas con aria-controls. Cuando un id solo conecta un label con su input, también puedes saltarte el id y anidar el input dentro del label (<label>Name <input /></label>); usa useId cuando los elementos no se pueden anidar.
Preguntas frecuentes
¿Qué hace useId en React?
Devuelve una cadena única para esa instancia del componente que se mantiene igual en cada renderizado. La usas para conectar elementos por id: htmlFor en un label, aria-describedby en un input, aria-labelledby en un diálogo.
¿Por qué no usar Math.random() o un contador para los ids?
Dan valores distintos en el servidor y en el navegador, así que una página renderizada en el servidor y su versión hidratada no coinciden y React reporta un desajuste de hidratación. Math.random() además cambia en cada renderizado. useId deriva el id de la posición del componente en el árbol, que es la misma en ambos lugares.
¿Puedo usar useId para las keys de una lista?
No. Una key debe venir de tus datos para que React pueda emparejar el mismo elemento entre renderizados. useId se llama una vez por componente, y llamarlo dentro de map rompe de todos modos las reglas de los hooks. Usa el id propio del elemento.
¿Cómo obtengo varios ids de una sola llamada a useId?
Llama a useId una vez y agrega sufijos: ${id}-name, ${id}-email. La base es única, así que las cadenas con sufijo también lo son.
¿Puedo usar el id de useId en un selector CSS?
Evítalo. El id sirve para conectar elementos en el DOM, y su formato exacto es un detalle interno que ha cambiado entre versiones de React. Da estilos con una clase, y busca los elementos con una ref en lugar de querySelector.