Dice UI
Utilities

Portal

Renders React elements into a different part of the DOM tree.

Installation

CLI

npx shadcn@latest add "https://diceui.com/r/portal"

Manual

Install the following dependencies:

npm install @radix-ui/react-slot

Copy and paste the following code into your project.

"use client";
 
import { Slot, type SlotProps } from "@radix-ui/react-slot";
import * as React from "react";
import * as ReactDOM from "react-dom";
 
interface PortalProps extends SlotProps {
  container?: Element | DocumentFragment | null;
}
 
const Portal = React.forwardRef<HTMLDivElement, PortalProps>(
  (props, forwardedRef) => {
    const { container: containerProp, ...portalProps } = props;
 
    const [mounted, setMounted] = React.useState(false);
 
    React.useLayoutEffect(() => {
      setMounted(true);
    }, []);
 
    const container =
      containerProp ?? (mounted ? globalThis.document?.body : null);
 
    if (!container) return null;
 
    return ReactDOM.createPortal(
      <Slot {...portalProps} ref={forwardedRef} />,
      container,
    );
  },
);
 
Portal.displayName = "Portal";
 
export { Portal };
 
export type { PortalProps };

Usage

import { Portal } from "@/components/portal"
 
export default function App() {
  return (
    <Portal>
      {/* Content to be rendered in a different part of the DOM */}
      <div>This will be rendered in the document body by default</div>
    </Portal>
  )
}

You can also specify a custom container:

import { Portal } from "@/components/portal"
 
export default function App() {
  const containerRef = useRef<HTMLDivElement>(null)
 
  return (
    <>
      <div ref={containerRef} />
      <Portal container={containerRef.current}>
        <div>This will be rendered in the custom container</div>
      </Portal>
    </>
  )
}

API Reference

Portal

A component that renders its children into a different part of the DOM tree using React's createPortal.

Props

PropTypeDefault
container
Element | DocumentFragment
document.body
asChild
boolean
false

On this page