Portal / PortalHost
Pre-alphaThe registry and the CLI are not published yet.Roadmap

Portal / PortalHost

Renders an element outside the current React Native hierarchy.

Installation

npx axiom add portal
pnpm dlx axiom add portal
yarn dlx axiom add portal
bun x axiom add portal

Installs: react-native-teleport.

Requirements

react-native-teleport supports the New Architecture (Fabric) only. It ships native code, so on Expo it needs a development build and won’t run in Expo Go.

What it does

Content rendered in a Portal appears in a PortalHost instead of where it is declared in the tree. Overlays use it so they draw above the rest of the screen, whatever the parent’s layout, clipping or z-order.

The copied portal file is a thin wrapper around react-native-teleport. The library moves the native views into the host, while the React tree stays where the Portal is declared. As a result:

  • context from the declaring component (theme, navigation, forms) still reaches the content;
  • state and refs survive a move between hosts;
  • nothing is re-rendered when the content moves.

Components never import react-native-teleport directly. They import @/components/core/portal, so replacing the library later only touches that one file.

Setup

Wrap your app once in PortalProvider and put a PortalHost after your navigation, so it draws above every screen:

app/_layout.tsx
import { PortalHost, PortalProvider } from "@/components/core/portal";

export default function RootLayout() {
	return (
		<PortalProvider>
			<Stack />
			<PortalHost />
		</PortalProvider>
	);
}

PortalHost fills the screen by default (StyleSheet.absoluteFill) and doesn’t intercept touches when it’s empty.

Usage

<Portal>
	<Toast />
</Portal>

Without hostName, content goes to the default root host. To target another host, give it a name:

<PortalHost name="toast" />

<Portal hostName="toast">
  <Toast />
</Portal>

Props

PortalProvider

PropTypeDefaultDescription
childrenReactNodeThe app. Every Portal and PortalHost must be inside it.

PortalHost

PropTypeDefaultDescription
namestring'root'Identifies the host. A Portal targets it through hostName.
styleStyleProp<ViewStyle>StyleSheet.absoluteFillHost container style. The host needs real dimensions, or its content won’t be visible.

Portal

PropTypeDefaultDescription
hostNamestring'root'Name of the target PortalHost. Changing it moves the content to the new host without re-rendering it.
namestringIdentifies this portal, so removePortal can remove it.
childrenReactNodeThe content to teleport.

Axiom changes one default from the library: there, Portal without hostName renders in place. Here it goes to root, because overlays are the main use case. Rendering in place isn’t exposed. If a component needs it, it renders its children without Portal.

usePortal

const { isHostAvailable, removePortal } = usePortal("toast");
Argument / returnTypeDescription
hostNamestringHost to watch. Defaults to 'root'.
isHostAvailablebooleanWhether a PortalHost with that name is mounted.
removePortal(name: string) => voidRemoves a portal from the host. Mounting a new portal with the same name shows it again.

Lifecycle

These rules come from react-native-teleport:

  • Host not mounted yet: the content renders where the Portal is declared, then moves into the host once it mounts.
  • Host unmounts: the content moves back to the Portal’s position. It isn’t destroyed and keeps its state.
  • Portal unmounts: its content unmounts with it, wherever it’s rendered.

An overlay that must never render inline (a toast in the middle of a list, for instance) checks isHostAvailable and renders nothing while the host is missing:

function Toast() {
	const { isHostAvailable } = usePortal();
	if (!isHostAvailable) return null;

	return (
		<Portal>
			<ToastContent />
		</Portal>
	);
}

Not supported

The wrapper only exposes the API above. Other react-native-teleport features, like moving an on-screen element into another container for shared transitions, stay available if you import the library yourself. Axiom components don’t use them.

Used by