What are portals in frameworks like React and Vue?
After working with different incarnations of the concept of portals in React and Vue I came to understand them as a way to differentiate the tree of UI components from the tree of logic components.
I know this definition may sound cryptic, but I promise you that it'll click if you read this post.
You see, components are a neat way to keep our code coherent. They encourage the delivery of vertical slices by grouping those pieces of state, logic, styles, and UI controls that work and change together.
That's why there's often a direct, one-to-one correlation between framework components and DOM nodes:
However, sometimes that's not what we want. The way we layout UI elements doesn't always align with the way how ownership, data and behavior are grouped.
Let me provide an example that illustrates this well. It's an example which we've all seen. It's a widget that opens a modal.
And yes, I am aware there are both third-party and built-in solutions that allow us to achieve something like that without thinking about it too much. I consider it to be a good example exactly because it's so common. We're all familiar with the end result, and that makes it easier to think about what's actually going on under the hood.
As far as ownership is concerned, the widget "owns" that modal. It decides when to show it and the modal doesn't have any other reason to exist than to serve the widget. Yet, the modal renders above everything else. It's one of those situations when the tree of components that render things on the screen doesn't align with the tree of components that define data and behavior.
So how can we code it?
We could reverse the invert hierarchy. The modal could become the parent of the widget. The modal would live in the root node of the entire application, and it'd pass the widget that opens it down. It could work, but it'd obscure what's more important - the widget - by placing some detail - the modal - above everything else. It'd also become harder for the widget to change what's inside of the modal.
What we're wrangling with here is that we clearly want the modal to render outside of the widget, yet the logical hierarchy should be such that the widget is the more important component that controls its modal.
That's exactly what portals are for! They enable us to decouple the DOM tree from the component tree. With portals we can teleport things from one place to another. Have a look:
The Widget
can still own and control the Modal
, but through the means of portals the Modal
renders outside of the Widget
.
Portals allow us to have the cake and eat it too. We can keep the hierarchy of components as readable and as predictable as possible, while still rendering UI elements that don't necessarily correspond to it one-to-one.