createPortal vous permet d’afficher certains enfants dans une partie différente du DOM.

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

Référence

createPortal(children, domNode, key?)

Pour créer un portail, appelez createPortal, en passant du JSX et le nœud DOM où il doit être affiché :

import { createPortal } from 'react-dom';

// ...

<div>
<p>Cet enfant est placé dans la div parente.</p>
{createPortal(
<p>Cet enfant est placé dans le corps du document.</p>,
document.body
)}
</div>

Voir plus d’exemples ci-dessous.

Un portail ne change que l’emplacement physique du nœud DOM. Pour tous les autres aspects, le JSX que vous placez dans un portail agit comme un nœud enfant du composant React qui l’affiche. Par exemple, l’enfant peut accéder au contexte fourni par l’arbre parent, et les événements remontent des enfants vers les parents selon l’arbre React.

Paramètres

  • children : quelque chose qui peut être affiché avec React, comme un morceau de JSX (par exemple <div /> ou <SomeComponent />), un Fragment (<>...</>), une chaîne de caractères ou un nombre, ou un tableau de tout ça.

  • domNode: un nœud DOM, comme ceux retournés par document.getElementById(). Le nœud doit déjà exister. Passer un nœud DOM différent lors d’une mise à jour entraînera la recréation du contenu du portail.

  • key optionnelle : une chaîne de caractères ou un nombre, unique, à utiliser comme clé du portail.

Valeur renvoyée

createPortal renvoie un nœud React qui peut être inclus dans le JSX ou renvoyé par un composant React. Si React le voit dans la sortie de l’affichage, il placera les children fournis à l’intérieur du domNode fourni.

Limitations

  • Les événements des portails se propagent en suivant l’arbre React, plutôt que l’arbre du DOM. Par exemple, si vous cliquez à l’intérieur d’un portail, et que le portail est situé dans une <div onClick>, ce gestionnaire onClick sera déclenché. Si ça pose des problèmes, arrêtez la propagation de l’événement à l’intérieur du portail, ou déplacez le portail lui-même plus haut dans l’arbre React.

Utilisation

Afficher dans une partie différente du DOM

Les portails permettent à vos composants d’afficher certains de leurs enfants dans un endroit différent du DOM. Ça permet à une partie de votre composant de « s’échapper » de tous les conteneurs dans lesquels elle peut se trouver. Par exemple, un composant peut afficher une boîte de dialogue modale ou une infobulle qui apparaît au-dessus et en-dehors du reste de la page.

Pour créer un portail, affichez le résultat de createPortal avec du JSX et le nœud DOM où il doit aller :

import { createPortal } from 'react-dom';

function MyComponent() {
return (
<div style={{ border: '2px solid black' }}>
<p>Cet enfant est placé dans la div parente.</p>
{createPortal(
<p>Cet enfant est placé dans le corps du document.</p>,
document.body
)}
</div>
);
}

React placera les nœuds DOM résultant du JSX que vous avez passé à l’intérieur du nœud DOM que vous avez fourni.

Sans portail, le second <p> serait placé à l’intérieur de la <div> parente, mais le portail l’a “téléporté” dans le document.body :

import { createPortal } from 'react-dom';

export default function MyComponent() {
  return (
    <div style={{ border: '2px solid black' }}>
      <p>Cet enfant est placé dans la div parente.</p>
      {createPortal(
        <p>Cet enfant est placé dans le corps du document.</p>,
        document.body
      )}
    </div>
  );
}

Observez comment le second paragraphe apparaît visuellement en-dehors de la <div> parente avec la bordure. Si vous inspectez la structure du DOM avec les outils de développement, vous verrez que le second <p> a été placé directement dans le <body> :

<body>
<div id="root">
...
<div style="border: 2px solid black">
<p>Cet enfant est placé dans la div parente.</p>
</div>
...
</div>
<p>Cet enfant est placé dans le corps du document.</p>
</body>

Un portail ne change que l’emplacement physique du nœud DOM. Pour tous les autres aspects, le JSX que vous placez dans un portail agit comme un nœud enfant du composant React qui l’affiche. Par exemple, l’enfant peut accéder au contexte fourni par l’arbre parent, et les événements remontent des enfants vers les parents selon l’arbre React.


Afficher une boîte de dialogue modale avec un portail

Vous pouvez utiliser un portail pour créer une boîte de dialogue modale qui flotte au-dessus du reste de la page, même si le composant qui appelle la boîte de dialogue est à l’intérieur d’un conteneur avec overflow: hidden ou d’autres styles qui interfèrent avec la boîte de dialogue.

Dans cet exemple, les deux conteneurs ont des styles qui perturbent la boîte de dialogue modale, mais celui qui est affiché via un portail n’est pas affecté car, dans le DOM, la boîte de dialogue modale n’est pas contenue dans les éléments JSX parents.

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>
    </>
  );
}

Piège

Il est important de s’assurer que votre application est accessible lors de l’utilisation de portails. Par exemple, vous devrez peut-être gérer le focus du clavier afin que l’utilisateur puisse déplacer le focus dans et hors du portail de manière naturelle.

Suivez les bonnes pratiques WAI-ARIA de création de modales lors de la création de modales. Si vous utilisez un module communautaire, assurez-vous qu’il est accessible et qu’il suit ces directives.


Afficher des composants React dans un balisage non-React issu du serveur

Les portails peuvent vous être utiles si votre racine React n’est qu’une partie d’une page statique ou produite côté serveur qui n’est pas construite avec React. Par exemple, si votre page est construite avec un framework côté serveur comme Rails, vous pouvez créer des zones d’interactivité dans des emplacements statiques telles que des barres latérales. Par rapport à l’utilisation de plusieurs racines React séparées, les portails vous permettent de traiter l’application comme un seul arbre React avec un état partagé, même si ses parties sont affichées dans différentes parties du 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>Cette partie est affichée par React</p>;
}

function SidebarContent() {
  return <p>Cette partie est aussi affichée par React !</p>;
}


Afficher des composants React dans des nœuds DOM qui ne sont pas gérés par React

Vous pouvez aussi utiliser un portail pour gérer le contenu d’un nœud DOM géré en-dehors de React. Par exemple, supposons que vous intégriez un widget de carte réalisé sans React et que vous souhaitiez afficher du contenu React dans une popup. Pour ce faire, déclarez une variable d’état popupContainer pour stocker le nœud DOM dans lequel vous allez effectuer l’affichage :

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

Lorsque vous créez le widget tiers, stockez le nœud DOM renvoyé par le widget afin de pouvoir y afficher du contenu par la suite :

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

Ça vous permet d’utiliser createPortal pour afficher du contenu React dans popupContainer une fois qu’il est disponible :

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

Voici un exemple complet avec lequel vous pouvez jouer :

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>Bonjour depuis React !</p>,
        popupContainer
      )}
    </div>
  );
}