Before going into my answer, I'll just note that I interpret the question as "in which cases is there no alternative to using a portal, or a portal would be a demonstrably much better solution?"
There are very few cases where portals are the only solution. A lot of the time there's a way to structure your app so that you don't need to use them. There's some niche use cases, but even there it's usually not the only solution.
For example in the SVG use case, you could instead create a Labels component that takes an SVG React element as argument, and then loops recursively over the children to construct an HTML element with matching labels in the right position. That would as a bonus also make the SVG code a lot simpler. If the SVG is user editable, you'd have to store its state as a whole anyway on each change, allowing you to easily pass the state back into both SVG and label elements. That said, here the portal solution seems at least on par with the alternatives, and could be the simplest in some circumstances.
Dispatching plugin components
Portals can be useful for library/framework authors. It allows plugins to render multiple components in the same element, each of which the framework then portals to a different position in the UI (e.g. editor area, sidebar).
WordPress's block editor uses this for a few things, they call it SlotFill. For example if you're coding a new block in a plugin. You always provide an edit
component for each block, which is rendered to the WYSIWYG editor. If it includes an InspectorControls
component, everything inside it will go into the sidebar.
The Block Toolbar works in the same way. Content of the <BlockControls/>
element is moved to the toolbar that is displayed right above the block content.
This is implemented as a portal to a registered slot.
The advantage of a portal here is that it allows a block's code to reuse the state and hooks in all components, even though they are not rendered in the same place. This makes the process of adding sidebar code very easy and with minimal repetition. If plugins instead needed to provide each of these components as a standalone React component, managing their state would be much more complex.
Example
You won't find ReactDOM.createPortal
in the example itself. It's rather an example of how a plugin can benefit from a framework that uses it. See WordPress's source code if you're interested in the implementation details.
I added a simple useState
hook to the mentioned InspectorControls example
and removed some irrelevant parts.
edit: ( { attributes, setAttributes } ) => {
const [myColor, setMyColor] = useState('#fff');
return (
<div>
<InspectorControls key="setting">
// Simplified HTML, real world blocks use more complex HTML here.
<ColorPalette
value={myColor}
onChange={ setMyColor}
/>
</InspectorControls>
<TextControl
value={ attributes.message }
onChange={ ( val ) => setAttributes( { message: val } ) }
style={ {
backgroundColor: myColor,
color: attributes.text_color,
} }
/>
</div>
);
},