Menu

Hook useId no React: ids únicos para labels e ARIA

useId retorna um id único e estável para cada instância de componente, para que um label, um input e uma dica com aria-describedby apontem uns para os outros mesmo quando o componente aparece muitas vezes. Entenda por que Math.random e contadores quebram com renderização no servidor e por que o useId não serve para keys de listas.

Esta página tem editores executáveis - edite, execute e veja a saída na hora.

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.

Ilustração das linguagens de programação do Coddy

Aprenda a programar com o Coddy

COMEÇAR