What is @gorhom/portal?
@gorhom/portal is a React Native package that allows you to create and manage portals. Portals provide a way to render children into a DOM node that exists outside the DOM hierarchy of the parent component. This is particularly useful for rendering modals, tooltips, and other UI elements that need to appear above other content.
What are @gorhom/portal's main functionalities?
Creating a Portal
This code demonstrates how to create a simple portal using @gorhom/portal. The portal renders a modal-like view that appears above the main content when a button is pressed.
import { PortalProvider, Portal } from '@gorhom/portal';
import React from 'react';
import { View, Text, Button } from 'react-native';
const App = () => {
const [visible, setVisible] = React.useState(false);
return (
<PortalProvider>
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Button title="Show Portal" onPress={() => setVisible(true)} />
{visible && (
<Portal>
<View style={{ position: 'absolute', top: 0, left: 0, right: 0, bottom: 0, backgroundColor: 'rgba(0,0,0,0.5)', justifyContent: 'center', alignItems: 'center' }}>
<Text style={{ color: 'white' }}>This is a portal!</Text>
<Button title="Close" onPress={() => setVisible(false)} />
</View>
</Portal>
)}
</View>
</PortalProvider>
);
};
export default App;
Nested Portals
This code demonstrates how to create nested portals using @gorhom/portal. The nested portal appears above the first portal when a button is pressed.
import { PortalProvider, Portal } from '@gorhom/portal';
import React from 'react';
import { View, Text, Button } from 'react-native';
const App = () => {
const [visible, setVisible] = React.useState(false);
const [nestedVisible, setNestedVisible] = React.useState(false);
return (
<PortalProvider>
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Button title="Show Portal" onPress={() => setVisible(true)} />
{visible && (
<Portal>
<View style={{ position: 'absolute', top: 0, left: 0, right: 0, bottom: 0, backgroundColor: 'rgba(0,0,0,0.5)', justifyContent: 'center', alignItems: 'center' }}>
<Text style={{ color: 'white' }}>This is a portal!</Text>
<Button title="Show Nested Portal" onPress={() => setNestedVisible(true)} />
{nestedVisible && (
<Portal>
<View style={{ position: 'absolute', top: 0, left: 0, right: 0, bottom: 0, backgroundColor: 'rgba(0,0,0,0.7)', justifyContent: 'center', alignItems: 'center' }}>
<Text style={{ color: 'white' }}>This is a nested portal!</Text>
<Button title="Close Nested" onPress={() => setNestedVisible(false)} />
</View>
</Portal>
)}
<Button title="Close" onPress={() => setVisible(false)} />
</View>
</Portal>
)}
</View>
</PortalProvider>
);
};
export default App;
Other packages similar to @gorhom/portal
react-native-modal
react-native-modal is a widely-used package for creating modals in React Native. It provides a lot of customization options and animations. Unlike @gorhom/portal, it is specifically designed for modals and does not support other types of portals.
react-native-paper
react-native-paper is a UI library that includes a Portal component among many other UI components. It is a more comprehensive solution for building React Native applications with Material Design. The Portal component in react-native-paper is similar to @gorhom/portal but is part of a larger UI toolkit.
react-native-root-siblings
react-native-root-siblings is another package that allows you to render elements outside the main React Native view hierarchy. It is similar to @gorhom/portal in that it can be used to create modals, toasts, and other overlay components. However, it is less focused on portals specifically and more on general overlay management.
React Native Portal
A simplified portal implementation for ⭕️ React Native ⭕️.
Features
- Multi portals handling.
- Multi portal hosts handling.
- Allow override functionality.
- Compatible with
React Native Web
. - Compatible with
Expo
, check out the project Expo Snack. - Written in
TypeScript
.
Installation
yarn add @gorhom/portal
Usage
Simple Portal
This is the very simple usage of this library, where you will teleport your content to the PortalProvider
layer of the app.
First, you will need to add PortalProvider
to your root component - this usually be the App.tsx
.
export const App = () => (
<PortalProvider>
{... your app goes here}
</PortalProvider>
);
Last, you wrap the content that you want to teleport with Portal
.
const BasicScreen = () => {
return (
{ ... }
<Portal>
<Text>
Text to be teleported to the root host
</Text>
</Portal>
{ ... }
);
};
Custom Portal Host
This is when you need to teleport your content to a custom portal host PortalHost
at any layer in the app.
First, you will need to add PortalProvider
to your root component - this usually be the App.tsx
.
export const App = () => (
<PortalProvider>
{... your app goes here ...}
</PortalProvider>
);
Second, you will need to add PortalHost
at any layer in your app, with a custom name.
const CustomView = () => {
return (
{ ... }
<PortalHost name="CustomPortalHost" />
{ ... }
);
};
Last, you wrap the content that you want to teleport with Portal
and the custom portal host name.
const BasicScreen = () => {
return (
{ ... }
<Portal hostName="CustomPortalHost">
<Text>
Text to be teleported to the CustomView component
</Text>
</Portal>
{ ... }
);
};
React Native Screens integration
In order to get your teleported content on top of all native views, you will need to wrap your content with FullWindowOverlay
from react-native-screens
.
import { FullWindowOverlay } from 'react-native-screens';
const BasicScreen = () => {
return (
{ ... }
<Portal>
<FullWindowOverlay style={StyleSheet.absoluteFill}>
<Text>
Text to be teleported to the CustomView component
</Text>
</FullWindowOverlay>
</Portal>
{ ... }
);
};
React Native Gesture Handler
To avoid issues when using the React Native Portal with React Native Gesture Handler, you must place the PortalProvider
under the GestureHandlerRootView
, otherwise it might freeze your app.
export const App = () => (
<GestureHandlerRootView>
<PortalProvider>
{... your app goes here}
</PortalProvider>
</GestureHandlerRootView>
);
Read more about the app freezing issue.
Props
Portal Props
name
Portal's key or name to be used as an identifer.
required:
NO | type:
string | default:
auto generated unique key
hostName
Host's key or name to teleport the portal content to.
required:
NO | type:
string | default:
'root'
handleOnMount
Override internal mounting functionality, this is useful if you want to trigger any action before mounting the portal content.
type handleOnMount = (mount?: () => void) => void;
required:
NO | type:
function | default:
undefined
handleOnUnmount
Override internal un-mounting functionality, this is useful if you want to trigger any action before un-mounting the portal content.
type handleOnUnmount = (unmount?: () => void) => void;
required:
NO | type:
function | default:
undefined
children
Portal's content.
required:
NO | type:
ReactNode | ReactNode[] | default:
undefined
PortalHost Props
name
Host's key or name to be used as an identifier.
required:
YES | type:
string
Hooks
usePortal
To access internal functionalities of all portals.
type usePortal = (hostName: string = 'root') => {
registerHost: () => void;
deregisterHost: () => void;
addPortal: (name: string, node: ReactNode) => void;
updatePortal: (name: string, node: ReactNode) => void;
removePortal: (name: string) => void;
};
Built With ❤️
Author
To keep this library maintained and up-to-date please consider sponsoring it on GitHub. Or if you are looking for a private support or help in customizing the experience, then reach out to me on Twitter @gorhom.
License
MIT