Portal
Renders its children elsewhere in the document, such as document.body, without breaking hydration of a server-rendered page.
Overview
<Portal> renders its children into another element, document.body unless you give a
container, the way React's createPortal does. It is what this library's overlays use:
Popover, Tooltip, the lists of Combobox and Autocomplete, and the toasts.
The difference from createPortal is the server. A portal has no server equivalent, so the usual
typeof window !== 'undefined' && createPortal(...) renders nothing on the server and the portal on
the browser's first render, and React reports a hydration mismatch. <Portal> renders fallback,
nothing by default, on the server and while the page hydrates, and portals its children once
hydration has finished. In a page rendered only in the browser it portals at once.
Basic usage
import { useState } from 'react'
import { Button, Portal } from '@chassis-ui/react'
export const Example = () => {
const [shown, setShown] = useState(false)
return (
<>
<Button color="primary" onClick={() => setShown((value) => !value)}>
{shown ? 'Hide' : 'Show'} the banner
</Button>
{shown && (
<Portal>
<div className="position-fixed bottom-0 end-0 m-lg p-md bg-primary fg-white">
Rendered into document.body
</div>
</Portal>
)}
</>
)
}Server rendering
Anything that refers to portaled content, such as an aria-controls or aria-describedby naming
an element inside it, should wait for it too, or the server's HTML names an id no element has. Read
useHydrated to know when the portal exists. See
Server-side rendering.
API
Portal
| Prop | Type | Default |
|---|---|---|
children | ReactNode | — |
| The content to render in the portal. | ||
container? | Element | DocumentFragment | null | — |
Where the children are portaled. Defaults to document.body. | ||
fallback? | ReactNode | — |
| Rendered in place until the portal exists: on the server and during hydration. Nothing by default. | ||