useId retorna um id único para cada instância de um componente, e o mesmo id em toda renderização. Use-o para ligar um <label> ao seu <input>, ou um input à sua dica por meio de aria-describedby, quando o componente pode aparecer mais de uma vez na página.
O mesmo componente renderiza duas vezes e recebe dois ids diferentes, impressos abaixo de cada campo. Clique no texto "Confirm password" e o foco pula para o segundo input, porque o htmlFor dele corresponde só ao id desse input. Escreva fixo id="password" no lugar de useId() e os dois labels apontam para o primeiro input.
A sintaxe
const id = useId();
O useId não recebe argumentos e retorna uma string. Chame-o no nível superior do componente, como todo hook. O formato exato é interno e já mudou entre versões: o React 18 produzia :r1:, e o React 19.2 produz _r_1_ para um componente renderizado primeiro no navegador e um id começando com _R_, montado a partir da posição na árvore, para um renderizado no servidor. Nunca o analise nem dependa do formato dele.
Por que não Math.random ou um contador
Ids de acessibilidade precisam coincidir entre o HTML que o servidor envia e a árvore que o React monta no navegador. As duas formas óbvias de criar um id falham nesse teste.
// 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++;
Com renderização no servidor (Next.js, React Router no modo framework, qualquer configuração com hydrateRoot), o servidor imprime id="field-4817" no HTML, a primeira renderização do navegador calcula field-0, e o React reporta uma divergência de hidratação. O useId monta o id a partir da posição do componente na árvore, que é idêntica nos dois lados.
Mesmo sem servidor, um id criado durante a renderização pode mudar a cada renderização. Este exemplo mostra a diferença sem servidor nenhum:
Clique no botão algumas vezes. O valor do useId fica parado enquanto o id do contador sobe a cada renderização, então tudo o que apontava para o id antigo (um aria-describedby, um label) agora aponta para nada. Envolver o contador em useState(() => nextId++) corrigiria as re-renderizações, mas não a divergência com o servidor.
Vários ids a partir de uma chamada
Um componente com vários campos não precisa de várias chamadas de useId. Gere uma base e adicione um sufixo por elemento.
Digite uma palavra sem @ no campo de email: o erro aparece e o aria-describedby do input aponta para ele, então um leitor de tela lê o erro quando o input recebe foco. Renderize <SignupForm /> duas vezes em App e cada cópia recebe a própria base de id.
Não serve para keys de listas
Keys e ids resolvem problemas diferentes. Uma key diz ao React qual item é qual entre renderizações, então precisa vir dos dados. O useId dá um id por instância de componente, e você não pode chamá-lo dentro de um 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} />)}
Quando os seus dados não têm id, crie um quando o item for criado (crypto.randomUUID() no event handler que o adiciona), não durante a renderização. A página sobre listas e keys explica por que a key precisa ficar com o item dela.
Várias raízes React em uma página
Se dois apps React separados renderizam na mesma página, os ids deles podem colidir. Dê um prefixo a cada raiz:
createRoot(document.getElementById('cart'), { identifierPrefix: 'cart-' });
createRoot(document.getElementById('chat'), { identifierPrefix: 'chat-' });
Com renderização no servidor, passe o mesmo identifierPrefix para o renderizador do servidor e para o hydrateRoot, para que os dois lados produzam os mesmos ids.
Erros comuns
Renderizar uma árvore diferente no servidor e no navegador. O useId depende da posição do componente, então um ramo como typeof window === 'undefined' ? <A /> : <B /> acima de um campo pode deslocar os ids entre as duas renderizações. Mantenha a árvore igual durante a hidratação e troque depois dela, em um efeito.
Procurar o elemento pelo id. document.getElementById(id) funciona, mas uma ref é o jeito do React de alcançar um nó do DOM e não precisa de id nenhum.
Usá-lo como valor aleatório. O id é único dentro do app, não aleatório, e é previsível a partir da árvore. Não o use para tokens de segurança, chaves de cache ou qualquer coisa guardada entre sessões.
Quando usar
Use o useId sempre que um componente reutilizável precisar de um atributo id: campos de formulário construídos a partir de um componente usado muitas vezes, um tooltip ligado com aria-describedby, um diálogo com aria-labelledby, abas com aria-controls. Quando um id só liga um label ao input, você também pode dispensar o id e colocar o input dentro do label (<label>Name <input /></label>); use o useId quando os elementos não puderem ser aninhados.
Perguntas frequentes
O que o useId faz no React?
Ele retorna uma string única para aquela instância do componente que continua a mesma em toda renderização. Você a usa para ligar elementos por id: htmlFor em um label, aria-describedby em um input, aria-labelledby em um diálogo.
Por que não usar Math.random() ou um contador para os ids?
Eles dão valores diferentes no servidor e no navegador, então uma página renderizada no servidor e a versão hidratada dela discordam e o React reporta uma divergência de hidratação. O Math.random() também muda a cada renderização. O useId deriva o id da posição do componente na árvore, que é a mesma nos dois lugares.
Posso usar o useId para keys em uma lista?
Não. Uma key precisa vir dos seus dados para que o React associe o mesmo item entre renderizações. O useId é chamado uma vez por componente, e chamá-lo dentro de um map quebra as regras dos hooks de qualquer jeito. Use o próprio id do item.
Como obtenho vários ids a partir de uma chamada de useId?
Chame o useId uma vez e adicione sufixos: ${id}-name, ${id}-email. A base é única, então as strings com sufixo também são.
Posso usar o id do useId em um seletor CSS?
Evite. O id serve para ligar elementos no DOM, e o formato exato dele é um detalhe interno que já mudou entre versões do React. Estilize com uma classe e encontre elementos com uma ref em vez de querySelector.