Skip to content

Portal

XS, under 350B gzipped

Renders children into document.body, deferred to a client-only mount.

Category
Overlays
Budget
xs, static display primitive
Runtime deps
None

Preview

Preview language

This is the exact fixture the conformance suite renders in CI. Switch the language to see the component mirror and reformat.

Install

npx shadcn@latest add https://kata-ui-rho.vercel.app/r/portal.json

Copies the source into your project. Pulls in 1 primitive: use-hydrated.

Source

"use client";

import { createPortal } from "react-dom";
import { useHydrated } from "../lib/use-hydrated";

/**
 * Renders `children` into `document.body` instead of the current DOM
 * position — what a dialog or toast needs so it is not visually clipped or
 * z-index-trapped by an ancestor with `overflow: hidden`.
 *
 * Deferred to a client-only mount: `document` does not exist during server
 * rendering, and portaling during the very first client render (before
 * hydration settles) can detach the node React expects to reconcile against.
 */
export function Portal({ children }: { children: React.ReactNode }) {
  const hydrated = useHydrated();

  if (!hydrated) return null;
  return createPortal(children, document.body);
}

What CI checks

Used by

← All components