Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions docs/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -3434,6 +3434,18 @@
}
]
]
},
{
"title": "Utilities",
"collapse": false,
"items": [
[
{
"title": "`<UNSAFE_PortalProvider>`",
"href": "/docs/reference/components/utilities/portal-provider"
}
]
]
}
]
]
Expand Down
82 changes: 82 additions & 0 deletions docs/reference/components/utilities/portal-provider.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
---
title: '`<UNSAFE_PortalProvider>`'
description: The <UNSAFE_PortalProvider> component allows Clerk floating UI elements to render inside custom containers instead of document.body.
sdk: astro, expo, nextjs, react, react-router, tanstack-react-start, vue
---

The `<UNSAFE_PortalProvider>` component allows you to specify a custom container for Clerk floating UI elements (popovers, modals, tooltips, etc.) that use portals. Only Clerk components within the provider will be affected, components outside the provider will continue to use the default `document.body` for portals.

This is particularly useful when using Clerk components inside external UI libraries like [Radix Dialog](https://www.radix-ui.com/primitives/docs/components/dialog) or [React Aria Components](https://react-spectrum.adobe.com/react-aria/components.html), where portaled elements need to render within the dialog's container to remain interactable.

> [!CAUTION]
> This component is marked as `UNSAFE` because it is an escape hatch that modifies the portal behavior of Clerk components. Not all portal locations will work correctly—some may cause issues with styling, accessibility, or other functionality. Typically, it is best to portal to the root of your application (e.g., the `body` element), outside of any possible overflow or stacking contexts. Use `<UNSAFE_PortalProvider>` only when necessary, such as when Clerk components are rendered inside external dialogs or popovers, or when you need to group all portalled elements into a single container at the root of your app.

## Usage

<If notSdk="vue">
### With Radix Dialog

A common use case is rendering Clerk components inside a Radix Dialog. Without `<UNSAFE_PortalProvider>`, the `<UserButton>` popover would render outside the dialog and may not be interactable.

```tsx {{ filename: 'app/components/UserDialog.tsx' }}
'use client'

import { useRef } from 'react'
import * as Dialog from '@radix-ui/react-dialog'
import { UNSAFE_PortalProvider, UserButton } from '@clerk/nextjs'

export function UserDialog() {
const containerRef = useRef<HTMLDivElement>(null)

return (
<Dialog.Root>
<Dialog.Trigger>Open Dialog</Dialog.Trigger>
<Dialog.Portal>
<Dialog.Overlay />
<Dialog.Content ref={containerRef}>
<UNSAFE_PortalProvider getContainer={() => containerRef.current}>
<UserButton />
</UNSAFE_PortalProvider>
</Dialog.Content>
</Dialog.Portal>
</Dialog.Root>
)
}
```
</If>

<If sdk="vue">
### With Reka UI Dialog

```vue {{ filename: 'UserDialog.vue' }}
<script setup>
import { useTemplateRef } from 'vue'
import { DialogContent } from 'reka-ui'
import { UNSAFE_PortalProvider, UserButton } from '@clerk/vue'
const dialogContentRef = useTemplateRef('dialogContentRef')
</script>
<template>
<DialogContent ref="dialogContentRef">
<UNSAFE_PortalProvider :getContainer="() => dialogContentRef?.$el">
<UserButton />
</UNSAFE_PortalProvider>
</DialogContent>
</template>
```
</If>

## Properties

<Properties>
- `getContainer`
- `() => HTMLElement | null`

A function that returns the container element where portals should be rendered. This function is called each time a portal needs to be rendered, so it should return a stable reference to the container element.

---

- `children`
- `React.ReactNode`

The Clerk components that should have their portals rendered into the custom container.
</Properties>