# Portal

Render children at the root, above navigation. The primitive behind every overlay.

[Documentation](https://eoria.adamtrip.pt/components/portal/)

[Registry source](https://eoria.adamtrip.pt/r/portal.json)

npm dependencies: none declared directly.

Registry dependencies: none.

```sh
npx @eoria/cli add portal
```

## Usage

```tsx
import { Portal, PortalHost } from '@/components/ui/portal'

// once, last in the root layout
<PortalHost />

// anywhere
<Portal modal>{overlay}</Portal>
```

## Notes

- A module store plus `useSyncExternalStore`, no native Modal. Same behaviour on every platform, and it never fights the navigator.
- `modal` marks the host `accessibilityViewIsModal` while mounted, which hides the rest of the app from VoiceOver.
- React context does not cross the portal. Components that need it re-provide it inside their portal content. Dialog, Popover, DropdownMenu and Select all do.
- The host measures its own window offset so anchored content can convert `measureInWindow` coordinates. It always mounts, with `pointerEvents="box-none"`, so it never blocks touches.

## Source

[`registry/ui/portal.tsx`](https://github.com/adamtrip-solutions/eoria/blob/main/registry/ui/portal.tsx)
