Um Fragment do React permite que um componente retorne vários elementos sem envolvê-los em uma <div> extra. Escreva <> antes do primeiro elemento e </> depois do último, e o React coloca os elementos direto no pai, sem nenhum nó próprio na página.
Cada Greeting retorna um título e um parágrafo lado a lado. Apague as linhas <> e </> e a prévia mostra um erro de build no lugar, porque agora a função tenta retornar dois valores.
Por que um componente precisa de uma raiz
O JSX é um jeito mais agradável de escrever chamadas de função. <h2>Hi</h2> compila para algo como jsx('h2', { children: 'Hi' }), uma expressão. Uma instrução return devolve um único valor, então duas tags irmãs sem nada em volta são como escrever return a b: o compilador para com "Adjacent JSX elements must be wrapped in an enclosing tag".
Antes de os fragments existirem, a solução era uma <div> envolvendo tudo. Funciona, mas a div é um elemento de verdade. Ela aparece no DOM, participa do layout CSS, e alguns pais nem a aceitam como filho. Um fragment cumpre a regra da raiz única e depois desaparece.
Dá para ver que ele não adiciona nada. O exemplo abaixo renderiza os mesmos dois itens por meio de um fragment e por meio de uma div e depois conta os filhos diretos de cada contêiner.
O console imprime 2 para o fragment, os dois spans, e 1 para a div, que os envolve. Em um contêiner flex ou grid, essa diferença decide se os spans são posicionados como itens ou agrupados em um só.
Sintaxe curta e o componente Fragment
Há duas formas de escrever um fragment, e as duas compilam para a mesma coisa:
import { Fragment } from 'react';
// Short syntax, no import needed
<>
<dt>React</dt>
<dd>A library for building user interfaces</dd>
</>
// Long form
<Fragment>
<dt>React</dt>
<dd>A library for building user interfaces</dd>
</Fragment>
Use <>...</> em quase todo lugar. Recorra a <Fragment> em um caso: quando o fragment precisa de uma key. A sintaxe curta não aceita atributos, então <key={id}> não é JSX válido. Você também pode ver <React.Fragment> em código antigo; é o mesmo componente, acessado pelo import padrão de React.
Fragments com key em listas
Quando você renderiza uma lista com map, cada item precisa de uma key para que o React o acompanhe entre renderizações (a página sobre listas e keys explica por quê). Se cada item é um grupo de elementos em vez de um só, a key vai no fragment, então você precisa da forma longa.
Uma lista de descrições é o caso clássico: cada entrada é um <dt> e um <dd>, e um <dl> deve contê-los diretamente.
Clique em Reverse e os pares trocam de lugar como unidades inteiras, porque cada par tem a própria key. Uma <div> envolvendo cada par também aceitaria uma key, e é HTML válido dentro de um dl, mas ela coloca um terceiro nível na marcação que o fragment evita.
Fragments dentro de tabelas
As tabelas são onde um wrapper perdido mais atrapalha. Um <tr> espera células <td> e <th> como filhos diretos. Se um componente que retorna várias células as envolve em uma <div>, o navegador trata a div como uma célula estranha e as células dentro dela deixam de se alinhar com o cabeçalho.
A linha com fragment tem o preço embaixo de Price e o estoque embaixo de Stock. Na linha com div, as duas células ficam espremidas na coluna Price e a coluna Stock fica vazia. Em desenvolvimento, o React também registra um aviso de que uma <div> não pode ser filha de um <tr>; a prévia roda como um build de produção, então pula essa verificação, e o que você vê aqui é o layout quebrado. Com renderização no servidor, o mesmo erro também pode causar erros de hidratação, porque o parser de HTML do navegador move a div fora do lugar.
A mesma regra vale para <ul> e <ol>, que devem conter elementos <li>, e para <select>, que deve conter elementos <option>. Sempre que um componente retornar partes que pertencem diretamente a um pai desses, retorne um fragment.
Fragments ou arrays
Um componente também pode retornar um array de elementos, e o React os renderiza em ordem como um fragment. Cada elemento de um array retornado precisa da própria key, assim como o resultado de um map, porque o React trata o array como uma lista:
function Cells() {
return [<td key="price">$4</td>, <td key="stock">In stock</td>];
}
Funciona, mas é mais poluído: vírgulas entre os elementos, uma key em cada um e um colchete fácil de perder de vista quando a marcação cresce. Use um fragment para um grupo fixo de elementos e deixe os arrays para o que eles são naturalmente, o resultado de mapear dados.
O que um fragment não consegue fazer
Como um fragment não renderiza nenhum elemento, ele não tem onde colocar uma classe, um estilo, um event handler ou uma ref. <Fragment className="row"> não faz nada útil, e a única prop que o React lê de um fragment é key. Quando você precisa estilizar o grupo ou ouvir cliques nele, precisa mesmo de um elemento de verdade, e tudo bem: escolha um que seja válido onde ele fica, como um <tbody> para agrupar linhas ou um <li> para guardar um item de lista.
Um fragment funciona em qualquer lugar onde um elemento funciona, inclusive dentro da renderização condicional: {show && <>...</>} mostra ou esconde o grupo inteiro de uma vez.
Perguntas frequentes
O que é um Fragment no React?
Um Fragment é um wrapper que permite a um componente retornar vários elementos como um único valor sem adicionar um nó ao DOM. <><h1 /><p /></> coloca o h1 e o p diretamente no pai.
Qual é a diferença entre <> e <Fragment>?
Os dois produzem a mesma coisa. <>...</> é a sintaxe curta. <Fragment> é a forma longa que você importa de react, e é a única que aceita uma key, necessária quando você renderiza fragments em uma lista.
Por que usar um Fragment em vez de uma div?
Uma div é um elemento de verdade. Ela pode quebrar layouts que esperam filhos diretos (contêineres flex e grid, ul, table, tr) e adiciona ruído ao DOM. Um Fragment não adiciona nada.
Um Fragment pode ter className ou onClick?
Não. Um Fragment não renderiza nenhum elemento, então não há onde colocar uma classe, um estilo ou um event handler. A única prop que ele aceita é key. Se você precisa de alguma dessas coisas, use um elemento de verdade.
Por que aparece "Adjacent JSX elements must be wrapped in an enclosing tag"?
Um componente retornou dois elementos irmãos sem nada em volta. O JSX compila para uma única expressão, então envolva os irmãos em <>...</>.