Portal
XS, under 350B gzippedRenders children into document.body, deferred to a client-only mount.
- Category
- Overlays
- Budget
- xs, static display primitive
- Runtime deps
- None
Preview
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.jsonCopies 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
- Bundled, minified and gzipped against its tier budget
- Audited by axe in the state previewed above
- Rendered through react-dom/server with no browser globals
- Scanned for network calls, dangerous sinks, and unguarded animation