Skip to main contentSkip to docs navigation

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

JSX
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

PropTypeDefault
childrenReactNode—
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.