Portal

Send items to other areas of the tree

Portal sends content to another part of the rendered tree while preserving Tamagui theme and configuration context.

Portal is included in tamagui and is used by Sheet, Dialog, Popover, Select, and Toast. See Stacking (zIndex) for how overlay layering works.

PortalProvider

TamaguiProvider from tamagui already wraps your app in a PortalProvider with a root PortalHost, so most apps need no setup. Add one yourself only when you render the bare TamaguiProvider from @tamagui/core:

import { TamaguiProvider } from '@tamagui/core'
import { PortalProvider } from '@tamagui/portal'
export function App() {
return (
<TamaguiProvider config={config}>
<PortalProvider shouldAddRootHost>{/* your app */}</PortalProvider>
</TamaguiProvider>
)
}

Render only one PortalProvider with a root host. Nesting a second one under TamaguiProvider from tamagui mounts two root hosts and causes hydration mismatches; pass shouldAddRootHost={false} if you need the nested provider.

On web, React’s built-in createPortal preserves context automatically. On native, the default portal implementation doesn’t preserve React context. Tamagui automatically re-propagates its own contexts (theme, configuration), but your custom contexts like navigation or app state won’t be available inside portaled content. The re-propagation also adds some overhead.

We recommend using react-native-teleport to solve this. It uses React Native’s native portal API to preserve context automatically.

Step 1: install react-native-teleport

yarn add react-native-teleport

Step 2: import the setup module

In your app’s entry file (index.js or App.tsx), before any Tamagui imports:

import '@tamagui/native/setup-teleport'

That’s it! All portal-using components will now preserve context automatically on native. Without native portals, your custom context from parent components won’t be available inside portaled content:

const MyContext = createContext('default')
function App() {
return (
<MyContext.Provider value="from-provider">
<Sheet modal open>
<Sheet.Container>
<Sheet.Background />
{/* Without native portal: you'd not have context here on native */}
<MyConsumer />
</Sheet.Container>
</Sheet>
</MyContext.Provider>
)
}

Alternative approaches

If you can’t use react-native-teleport, there are other ways to handle context in portals:

Component scoping

For Dialog, Popover, and Tooltip, use the scope prop to mount a single instance at your app root. This avoids portals entirely on native:

// _layout.tsx - mount once at root with all your providers
<TamaguiProvider>
<Tooltip scope="global">
<Tooltip.Content>
<Tooltip.Arrow />
<Paragraph>{/* label set by trigger */}</Paragraph>
</Tooltip.Content>
{/* rest of your app */}
<Slot />
</Tooltip>
</TamaguiProvider>
// anywhere in your app - just the trigger
<Tooltip.Trigger scope="global" aria-label="Settings">
<Button icon={Settings} />
</Tooltip.Trigger>

This pattern is also a performance win for lists or tables with many interactive elements.

Manual context re-propagation

Wrap portal children with the necessary providers:

const theme = useTheme()
const myValue = useContext(MyContext)
<Sheet>
<Sheet.Container>
<Sheet.Background />
<ThemeProvider theme={theme}>
<MyContext.Provider value={myValue}>
{/* content that needs context */}
</MyContext.Provider>
</ThemeProvider>
</Sheet.Container>
</Sheet>

This is more verbose and error-prone as you need to remember to re-propagate every context you use.

API reference

Portal

Props

  • zIndex

    number

    Fixed z-index value for the portal.

  • stackZIndex

    boolean | number | 'global'

    Enable automatic z-index stacking. Tamagui intelligently stacks z-index both horizontally (based on mount order) and vertically (nested content is always above parent but below sibling content that comes after). true enables stacking, a number adds to the stacked value, 'global' only stacks horizontally without nesting logic.

  • passThrough

    boolean

    When true, renders children directly without portal behavior.

  • This automatic stacking is already enabled by default in Dialog, Popover, Sheet, and other overlay components. If you open a Popover from within a Dialog, the Popover will automatically have a higher z-index than the Dialog without any configuration needed.

    PortalProvider

    Props

  • shouldAddRootHost

    boolean

    Default: 

    true

    Mounts a PortalHost at the end of the provider for portals to render into.

  • rootHostName

    string

    Default: 

    'root'

    Name of the root PortalHost.

  • Technical details

    react-native-teleport uses ReactNativeFabricUIManager.createPortal (Fabric) or UIManager.createPortal (Paper) to create true native portals that preserve the React fiber tree.

    The default portal implementation, by contrast, uses a JS-based approach with context providers and a reducer to manage portal state. While compatible with older RN versions, it breaks React context because it re-renders content in a separate provider tree.

    Tamagui includes a needsPortalRepropagation() helper that returns true when using the default portal implementation and false when using native portals, so library authors can conditionally re-propagate context only when needed.