createPortal permite que você renderize alguns filhos em uma parte diferente do DOM.

<div>
<SomeComponent />
{createPortal(children, domNode, key?)}
</div>

Referência

createPortal(children, domNode, key?)

Para criar um portal, chame createPortal, passando algum JSX e o nó do DOM onde ele deve ser renderizado:

import { createPortal } from 'react-dom';

// ...

<div>
<p>This child is placed in the parent div.</p>
{createPortal(
<p>This child is placed in the document body.</p>,
document.body
)}
</div>

Veja mais exemplos abaixo.

Um portal só muda a colocação física do nó do DOM. Em todas as outras maneiras, o JSX que você renderiza em um portal atua como um nó filho do componente React que o renderiza. Por exemplo, o filho pode acessar o contexto fornecido pela árvore pai, e eventos propagam-se de filhos para pais de acordo com a árvore React.

Parâmetros

  • children: Qualquer coisa que possa ser renderizada com o React, como um pedaço de JSX (por exemplo, <div /> ou <SomeComponent />), um Fragmento (<>...</>), uma string ou um número, ou uma array disso.

  • domNode: Algum nó do DOM, como aqueles retornados por document.getElementById(). O nó já deve existir. Passar um nó DOM diferente durante uma atualização fará com que o conteúdo do portal seja recriado.

  • opcional key: Uma string ou número único a ser usado como a chave do portal.

Retorna

createPortal retorna um nó React que pode ser incluído em JSX ou retornado de um componente React. Se o React o encontrar na saída de renderização, ele colocará os children fornecidos dentro do domNode fornecido.

Ressalvas

  • Eventos de portais se propagam de acordo com a árvore React, em vez da árvore DOM. Por exemplo, se você clicar dentro de um portal, e o portal for encapsulado em <div onClick>, esse manipulador onClick será acionado. Se isso causar problemas, pare a propagação do evento de dentro do portal, ou mova o próprio portal para cima na árvore React.

Uso

Renderizando para uma parte diferente do DOM

Portais permitem que seus componentes renderizem alguns de seus filhos em um lugar diferente no DOM. Isso permite que parte do seu componente “escape” de quaisquer contêineres em que ele possa estar. Por exemplo, um componente pode exibir um diálogo modal ou uma dica de ferramenta que aparece acima e fora do restante da página.

Para criar um portal, renderize o resultado de createPortal com algum JSX e o nó do DOM para onde ele deve ir:

import { createPortal } from 'react-dom';

function MyComponent() {
return (
<div style={{ border: '2px solid black' }}>
<p>This child is placed in the parent div.</p>
{createPortal(
<p>This child is placed in the document body.</p>,
document.body
)}
</div>
);
}

React colocará os nós DOM para o JSX que você passou dentro do nó DOM que você forneceu.

Sem um portal, o segundo <p> seria colocado dentro da <div> pai, mas o portal o “teletransportou” para o document.body:

import { createPortal } from 'react-dom';

export default function MyComponent() {
  return (
    <div style={{ border: '2px solid black' }}>
      <p>This child is placed in the parent div.</p>
      {createPortal(
        <p>This child is placed in the document body.</p>,
        document.body
      )}
    </div>
  );
}

Observe como o segundo parágrafo aparece visualmente fora da <div> pai com a borda. Se você inspecionar a estrutura do DOM com ferramentas de desenvolvedor, você verá que o segundo <p> foi colocado diretamente no <body>:

<body>
<div id="root">
...
<div style="border: 2px solid black">
<p>This child is placed inside the parent div.</p>
</div>
...
</div>
<p>This child is placed in the document body.</p>
</body>

Um portal muda apenas a colocação física do nó do DOM. De todas as outras maneiras, o JSX que você renderiza em um portal atua como um nó filho do componente React que o renderiza. Por exemplo, o filho pode acessar o contexto fornecido pela árvore pai, e os eventos ainda se propagam de filhos para pais de acordo com a árvore React.


Renderizando um diálogo modal com um portal

Você pode usar um portal para criar um diálogo modal que flutua acima do restante da página, mesmo que o componente que invoca o diálogo esteja dentro de um contêiner com overflow: hidden ou outros estilos que interferem no diálogo.

Neste exemplo, os dois contêineres têm estilos que interrompem o diálogo modal, mas o que é renderizado em um portal não é afetado porque, no DOM, o modal não está contido dentro dos elementos JSX pai.

import NoPortalExample from './NoPortalExample';
import PortalExample from './PortalExample';

export default function App() {
  return (
    <>
      <div className="clipping-container">
        <NoPortalExample  />
      </div>
      <div className="clipping-container">
        <PortalExample />
      </div>
    </>
  );
}

Pitfall

É importante garantir que seu aplicativo seja acessível ao usar portais. Por exemplo, pode ser necessário gerenciar o foco do teclado para que o usuário possa mover o foco para dentro e para fora do portal de uma maneira natural.

Siga as Práticas de Criação de Modal WAI-ARIA ao criar modais. Se você usar um pacote da comunidade, certifique-se de que ele seja acessível e siga essas diretrizes.


Renderizando componentes React em marcação de servidor não React

Portais podem ser úteis se sua raiz React for apenas parte de uma página estática ou renderizada no servidor que não é construída com React. Por exemplo, se sua página for construída com uma estrutura de servidor como Rails, você pode criar áreas de interatividade dentro de áreas estáticas, como barras laterais. Em comparação com ter várias raízes React separadas, portais permitem que você trate o aplicativo como uma única árvore React com estado compartilhado, mesmo que suas partes renderizem em diferentes partes do DOM.

import { createPortal } from 'react-dom';

const sidebarContentEl = document.getElementById('sidebar-content');

export default function App() {
  return (
    <>
      <MainContent />
      {createPortal(
        <SidebarContent />,
        sidebarContentEl
      )}
    </>
  );
}

function MainContent() {
  return <p>This part is rendered by React</p>;
}

function SidebarContent() {
  return <p>This part is also rendered by React!</p>;
}


Renderizando componentes React em nós DOM não React

Você também pode usar um portal para gerenciar o conteúdo de um nó DOM que é gerenciado fora do React. Por exemplo, suponha que você esteja integrando com um widget de mapa que não é React e deseja renderizar o conteúdo React dentro de um popup. Para fazer isso, declare uma variável de estado popupContainer para armazenar o nó DOM em que você vai renderizar:

const [popupContainer, setPopupContainer] = useState(null);

Quando você cria o widget de terceiros, armazene o nó DOM retornado pelo widget para poder renderizar nele:

useEffect(() => {
if (mapRef.current === null) {
const map = createMapWidget(containerRef.current);
mapRef.current = map;
const popupDiv = addPopupToMapWidget(map);
setPopupContainer(popupDiv);
}
}, []);

Isso permite que você use createPortal para renderizar o conteúdo React em popupContainer assim que ele estiver disponível:

return (
<div style={{ width: 250, height: 250 }} ref={containerRef}>
{popupContainer !== null && createPortal(
<p>Hello from React!</p>,
popupContainer
)}
</div>
);

Aqui está um exemplo completo com o qual você pode brincar:

import { useRef, useEffect, useState } from 'react';
import { createPortal } from 'react-dom';
import { createMapWidget, addPopupToMapWidget } from './map-widget.js';

export default function Map() {
  const containerRef = useRef(null);
  const mapRef = useRef(null);
  const [popupContainer, setPopupContainer] = useState(null);

  useEffect(() => {
    if (mapRef.current === null) {
      const map = createMapWidget(containerRef.current);
      mapRef.current = map;
      const popupDiv = addPopupToMapWidget(map);
      setPopupContainer(popupDiv);
    }
  }, []);

  return (
    <div style={{ width: 250, height: 250 }} ref={containerRef}>
      {popupContainer !== null && createPortal(
        <p>Hello from React!</p>,
        popupContainer
      )}
    </div>
  );
}