↗
Portal
iOS
Android
Web
Render UI outside the normal view hierarchy using a PortalHost. Perfect for overlays, modals, toasts, and tooltips that need to escape clipping or render above all other content.
Import
tsx
import { Portal, PortalHost } from 'react-native-cross-elements';
Interactive demo
portal.tsx
Show Toast
How it works
Mount a <PortalHost /> at the root of your app. Any <Portal> rendered anywhere in the tree will inject its children into the host's absolute, top-layer container (zIndex 1000).
⚠️
The host container has pointerEvents: 'none'. You must add pointerEvents: 'auto' to your overlay's outermost View to receive touches/clicks.
Setup — mount PortalHost
tsx
// app/_layout.tsx (or your root layout) import { PortalHost } from 'react-native-cross-elements'; export default function RootLayout({ children }) { return ( <View style={{ flex: 1 }}> {/* Mount PortalHost at the top level so portals render above everything */} <PortalHost /> {children} </View> ); }
Toast example
tsx
import React from 'react'; import { View, Text } from 'react-native'; import { Portal } from 'react-native-cross-elements'; export function ToastDemo() { const [message, setMessage] = React.useState<string | null>(null); React.useEffect(() => { const show = setInterval(() => setMessage('Saved ✅'), 5000); const hide = setInterval(() => setMessage(null), 6500); return () => { clearInterval(show); clearInterval(hide); }; }, []); return ( <Portal> {message && ( <View style={{ position: 'absolute', bottom: 24, left: 0, right: 0, alignItems: 'center', // Important: enable touches for portal content pointerEvents: 'auto', }} > <View style={{ paddingVertical: 10, paddingHorizontal: 20, borderRadius: 10, backgroundColor: '#18181b', borderWidth: 1, borderColor: '#27272a', }} > <Text style={{ color: '#e4e4e7' }}>{message}</Text> </View> </View> )} </Portal> ); }
Popover / anchored overlay
tsx
import React from 'react'; import { View, Text, Pressable } from 'react-native'; import { Portal } from 'react-native-cross-elements'; export function PopoverDemo() { const [visible, setVisible] = React.useState(false); return ( <View style={{ padding: 24 }}> <Pressable onPress={() => setVisible((v) => !v)}> <Text style={{ color: '#818cf8' }}>Toggle popover</Text> </Pressable> <Portal> {visible && ( <View style={{ position: 'absolute', top: 120, left: 24, pointerEvents: 'auto', }} > <View style={{ padding: 12, backgroundColor: '#18181b', borderRadius: 10, borderWidth: 1, borderColor: '#27272a', }} > <Text style={{ color: 'white' }}>I'm a popover rendered outside the tree</Text> </View> </View> )} </Portal> </View> ); }
Multiple named hosts
Mount multiple hosts with different name props and target them with portalName on the Portal. Default name is 'root_ui_portal'.
tsx
// Multiple named hosts at the root <PortalHost name="top_layer" /> <PortalHost name="hud" /> // Target a specific host <Portal portalName="hud"> {/* HUD content */} </Portal> <Portal portalName="top_layer"> {/* Modal content */} </Portal>
Dropdown integration
ℹ️
The Dropdown component auto-detects a mounted PortalHost and renders its window into it. Without a PortalHost it falls back to a native Modal.