Hook useId
Genera una ID única y estable para atributos de accesibilidad que sea consistente entre servidor y cliente.
Receta
Tarjeta de referencia rápida — lista para copiar y pegar.
const id = useId();
// Usa para elementos de formulario accesibles
<label htmlFor={id}>Email</label>
<input id={id} type="email" />
// Deriva múltiples IDs de una
<label htmlFor={`${id}-first`}>First Name</label>
<input id={`${id}-first`} />
<label htmlFor={`${id}-last`}>Last Name</label>
<input id={`${id}-last`} />Cuándo usarlo: Necesitas una ID única para htmlFor, aria-labelledby, aria-describedby, o cualquier atributo DOM que requiera una ID — especialmente en SSR o componentes reutilizables.
Ejemplo Funcional
"use client";
import { useId, useState } from "react";
interface TextFieldProps {
label: string;
helpText?: string;
}
export function TextField({ label, helpText }: TextFieldProps) {
const id = useId();
const helpId = `${id}-help`;
const [value, setValue] = useState("");
return (
<div className="space-y-1">
<label htmlFor={id} className="block text-sm font-medium">
{label}
</label>
<input
id={id}
value={value}
onChange={(e) => setValue(e.target.value)}
aria-describedby={helpText ? helpId : undefined}
className="border rounded px-3 py-2 w-full"
/>
{helpText && (
<p id={helpId} className="text-xs text-gray-500">
{helpText}
</p>
)}
</div>
);
}
export function SignupForm() {
return (
<form className="space-y-4 max-w-sm">
<TextField label="Email" helpText="We'll never share your email." />
<TextField label="Password" helpText="At least 8 characters." />
<TextField label="Username" />
</form>
);
}Lo que demuestra esto:
- Cada instancia de
TextFieldobtiene su propia ID única deuseId - La ID vincula
<label>a<input>mediantehtmlFory vincula el texto de ayuda mediantearia-describedby - Múltiples instancias del mismo componente coexisten sin colisiones de IDs
- Las IDs son estables entre renderizados de servidor y cliente, evitando desincronización de hidratación
Exploración Profunda
Cómo Funciona
useIdgenera una ID basada en la posición del componente en el árbol de React- La ID es determinística — el mismo componente en la misma posición del árbol siempre obtiene la misma ID
- La cadena generada incluye un carácter
:(por ejemplo,:r1:) para evitar colisiones con IDs definidas por el usuario - En el servidor, React genera las mismas IDs que en el cliente, asegurando consistencia de hidratación
- Múltiples llamadas a
useIden el mismo componente producen diferentes IDs
Parámetros y Valores de Retorno
| Parámetro | Tipo | Descripción |
|---|---|---|
| (ninguno) | — | useId no toma parámetros |
| Retorno | Tipo | Descripción |
|---|---|---|
id | string | Cadena de ID única (por ejemplo, :r1:, :r2:) |
Variaciones
Listbox accesible:
function Listbox({ label, options }: ListboxProps) {
const id = useId();
const labelId = `${id}-label`;
const listId = `${id}-list`;
return (
<div>
<span id={labelId}>{label}</span>
<ul id={listId} role="listbox" aria-labelledby={labelId}>
{options.map((opt, i) => (
<li key={opt.value} id={`${id}-option-${i}`} role="option">
{opt.label}
</li>
))}
</ul>
</div>
);
}identifierPrefix personalizado para micro-frontends:
// En createRoot o hydrateRoot
const root = createRoot(container, {
identifierPrefix: "app1-",
});
// IDs generadas: ":app1-r1:", ":app1-r2:", etc.Notas de TypeScript
// useId siempre devuelve una cadena — no se necesita genérico
const id: string = useId();
// Al construir una librería de componentes, acepta una ID opcional para sobrescribir
interface InputProps {
id?: string;
label: string;
}
function Input({ id: propId, label }: InputProps) {
const generatedId = useId();
const inputId = propId ?? generatedId;
return (
<>
<label htmlFor={inputId}>{label}</label>
<input id={inputId} />
</>
);
}Trampas
-
No uses para keys de lista —
useIdgenera una única ID por llamada de hook, no por elemento de lista. No es adecuado para propskey. Solución: Usa keys impulsadas por datos (item.id) o identificadores estables para keys de lista. -
Las IDs contienen dos puntos — El formato de ID generado (
:r1:) incluye dos puntos, que son válidos en atributosidHTML pero pueden causar problemas con selectores CSS como#\:r1\:. Solución: Usa selectores de atributo[id="value"]o escapa los dos puntos en CSS. -
No se puede usar condicionalmente — Como todos los hooks,
useIdno se puede llamar dentro de condiciones o loops. Solución: Llama auseIdal nivel superior y deriva sub-IDs con concatenación de cadenas. -
Múltiples raíces sin prefijo — Dos raíces React separadas en la misma página pueden generar IDs en conflicto. Solución: Usa la opción
identifierPrefixencreateRootohydrateRoot.
Alternativas
| Alternativa | Usa Cuando | No Uses Cuando |
|---|---|---|
crypto.randomUUID() | App solo en cliente, sin SSR | SSR está involucrado — causa desincronización de hidratación |
| ID basada en contador | Fuera de React (funciones de utilidad) | Dentro de componentes — no es determinística entre servidor/cliente |
useRef con inicialización perezosa | Necesitas un valor aleatorio estable, no una ID basada en posición de árbol | Necesitas atributos de accesibilidad seguros para SSR |
Envolvimiento de <label> HTML | Label envuelve directamente el input, sin ID necesaria | Input y label están separados en el DOM |
¿Por qué no solo usar una ID aleatoria? Las IDs aleatorias generadas durante el renderizado difieren entre servidor y cliente, causando desincronización de hidratación y advertencias de React. useId resuelve esto derivando IDs de la posición del árbol de componentes.
FAQs
¿Por qué no puedo simplemente usar Math.random() o crypto.randomUUID() para IDs de elementos?
- Las IDs aleatorias generadas durante el renderizado difieren entre servidor y cliente, causando desincronización de hidratación.
useIdderiva IDs de la posición del componente en el árbol de React, por lo que servidor y cliente producen la misma ID.- Usa
useIdsiempre que SSR esté involucrado.
¿Puedo usar useId para generar keys para elementos de lista?
- No.
useIdgenera una ID por llamada de hook, no por elemento de lista. - Está diseñado para atributos de accesibilidad (
htmlFor,aria-labelledby), no para propskey. - Usa keys impulsadas por datos (
item.id) o identificadores estables para listas.
¿Cómo derivo múltiples IDs de una única llamada a useId?
const id = useId();
const nameId = `${id}-name`;
const emailId = `${id}-email`;
const helpId = `${id}-help`;- Usa concatenación de cadenas para crear sub-IDs de una única llamada a
useId.
Trampa: ¿Por qué las IDs generadas contienen dos puntos y puede eso causar problemas en CSS?
useIdgenera IDs como:r1:con dos puntos para evitar colisiones con IDs definidas por el usuario.- Los dos puntos son válidos en atributos
idHTML pero deben escaparse en selectores CSS:#\:r1\:. - Prefiere selectores de atributo
[id="value"]en lugar de selectores de ID en CSS.
¿Qué sucede si dos raíces React separadas en la misma página generan IDs en conflicto?
- Sin configuración, dos raíces pueden producir las mismas IDs (
:r1:,:r2:, etc.). - Usa la opción
identifierPrefixencreateRootohydrateRootpara espaciar nombres de IDs.
const root = createRoot(container, { identifierPrefix: "app1-" });
// IDs: ":app1-r1:", ":app1-r2:", etc.¿Puedo llamar a useId condicionalmente o dentro de un loop?
- No. Como todos los hooks,
useIddebe ser llamado al nivel superior de tu componente. - Llámalo una vez y deriva sub-IDs con concatenación de cadenas para elementos condicionales o repetidos.
¿Cómo escribo useId en TypeScript?
// useId siempre devuelve cadena -- no se necesita genérico
const id: string = useId();- No hay parámetro genérico. El tipo de retorno siempre es
string.
¿Cómo construyo un componente reutilizable que acepta un prop de id opcional pero regresa a useId?
interface InputProps {
id?: string;
label: string;
}
function Input({ id: propId, label }: InputProps) {
const generatedId = useId();
const inputId = propId ?? generatedId;
return (
<>
<label htmlFor={inputId}>{label}</label>
<input id={inputId} />
</>
);
}Trampa: ¿El formato de ID generado es estable entre versiones de React?
- El formato exacto (por ejemplo,
:r1:) es un detalle de implementación y puede cambiar entre versiones de React. - No analices ni dependas de la estructura interna de la ID generada.
- Trátala como una cadena opaca.
¿Llamar a useId múltiples veces en el mismo componente produce diferentes IDs?
- Sí. Cada llamada a
useId()en el mismo componente devuelve una ID única. - Esto es útil cuando un componente necesita múltiples atributos de ID no relacionados.
- Todas las IDs generadas permanecen estables entre renderizados de servidor y cliente.
Relacionado
- useRef — accede a elementos DOM directamente sin IDs
- Custom Hooks — construye primitivas de formulario accesibles usando
useId - use — primitivas de React 19 que funcionan junto a
useId