Getting Started
App Providerglobal.d.tsThemesTypes
cn(...args)Text ParserUnits Converters
AnchorAvatarBreadcrumbBurgerButtonCardCarouselCheckerCodeColor PickerCommandConfettiCopyButtonDouble Helix WordsFloating IndicatorGroupHighlight TextIndicatorInputKbdLabelLoaderPaginationPassword RequirementPolymorphic SlotProgressProseRatingRunning AreaScroll AreaSheetsSkeletonSliderStackSvgTableTabsTextareaTimelineTimesToasterTooltipTyping WordsTypography
useClickOutsideuseClipboarduseDeviceInfouseDialoguseDidUpdateuseDirectionuseDisclosureuseDocumentTitleuseDocumentVisibilityuseElementInfouseEyeDropperuseFetchuseFullscreenuseGeoLocationuseHotkeysuseHoveruseIduseImagePopupuseInputStateuseIntersectionuseIntervaluseIsomorphicEffectuseListStateuseLocalStorageuseMeasureScrollbaruseMediaQueryuseMergedRefuseMouseuseMoveuseMutationObserveruseNetworkuseOpenStateuseOrientationuseOSusePaginationusePWAInstalleruseRandomColorsuseReducedMotionuseReloaduseResizeObserveruseScrollIntoViewuseStateHistoryuseTimeoutuseTouchuseTriggeruseUncontrolleduseValidatedStateuseViewportSizeuseWindowEventuseWindowScroll
Docs
Web
Components
Polymorphic Slot
Password Requirement

Shows live validation feedback for password requirements.

Progress

Visualizes progress with optional multi-section support.


Edit this page on GitHub
  • Started
  • Utilities
  • Configuration
  • Components
  • Hooks
  • Examples
  • Github
  • Contributing
⌘+J

© 2025 oeri rights MIT


Designed in Earth-616

Built by oeri

Polymorphic Slot

A flexible component that allows dynamic rendering of its children as different HTML elements or components, while preserving props and context.

Usage

Basic usage example to quickly see how the PolymorphicSlot works.

import { PolymorphicSlot } from "@/ui/polymorphic-slot";
import { buttonStyle } from "@/ui/button";

export function PolymorphicDemo() {
  return (
    <PolymorphicSlot className={buttonStyle({ variant: "outline", color: "grape" }, "w-4 h-4")}>
      <button type="button" className="w-max h-9 rounded-lg">
        My Button
      </button>
    </PolymorphicSlot>
  );
}

API References

  • cloneElement()
  • inspired by radix Slot

Combines and passing data with a render props onto its immediate child.

Support

  • The asChild prop will pass all the props you created yourself even the Slot ref to its direct child.
  • Using ref, attributes and event handlers according to the el="" you use,
  • Freestyle variables are used without affecting the default style

Alternative

By default we use twMerge to prioritize the tailwindcss style of PolymorphicSlot over its immediate child style. You can customize it at any time if you are not using tailwindcss.

If you prefer to use Slot from radix you can combine Polymorphic with radix dependencies using the following command:

  1. Install @radix-ui/react-slot
  2. Import dependencies and Customize PolymorphicSlot:
import * as React from "react";
import { Slot as PolymorphicSlot } from "@radix-ui/react-slot";
 
const Slot = <T extends React.ElementType = "div">({ asChild = false, el, ...props }: ElementType<T>, ref: PolymorphicSlotRef<T>) => {
  const Component = asChild ? PolymorphicSlot : ((el || "div") as React.ElementType);
 
  return <Component ref={ref} {...props} />;
};

If you don't need the element to pass its props, you can simplify it in a nutshell as below:

import * as React from "react";
 
export type PolymorphicRef<T extends React.ElementType> = React.ComponentPropsWithRef<T>["ref"];
export type PolymorphicWithoutRef<T extends React.ElementType, Exclude extends string = never> = Omit<React.ComponentProps<T>, "ref" | "style" | Exclude> & {
  el?: T;
  style?: React.CSSProperties & {
    [key: string]: any;
  };
};
 
const Element = <T extends React.ElementType = "div">({ el, ...props }: PolymorphicWithoutRef<T>, ref: PolymorphicRef<T>) => {
  const Component = (el || "div") as React.ElementType;
  return <Component ref={ref} {...props} />;
};
export default React.forwardRef(Element) as <T extends React.ElementType = "div">(props: PolymorphicWithoutRef<T> & { ref?: PolymorphicRef<T> }) => React.ReactElement | null;

ref and all property default as div

function MyComponent() {
  return <Polymorphic />;
}
(property) JSX.IntrinsicElements.div: DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement>

function MyComponent() {
  return <Polymorphic el="a" href="" />;
}
(property) JSX.IntrinsicElements.a: React.DetailedHTMLProps<React.AnchorHTMLAttributes<HTMLAnchorElement>, HTMLAnchorElement>

function MyComponent() {
  return <Polymorphic el="img" src="" />;
}
(property) JSX.IntrinsicElements.img: React.DetailedHTMLProps<React.ImgHTMLAttributes<HTMLImageElement>, HTMLImageElement>

function MyComponent() {
  return (
    <Polymorphic
      el="nav"
      style={{
        width: "var(--sz)",
        height: "var(--sz)",
        "--sz": "0.75rem",
        "--bg-open": "var(--background)"
      }}
    />
  );
}
(property) JSX.IntrinsicElements.nav: React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement>

Passing ref and attributes to element or component as a single child

function MyComponent() {
  const containerRef = useRef < HTMLElement > null; // if usage with ref
 
  return (
    <Polymorphic
      ref={containerRef}
      asChild
      style={{
        width: "var(--sz)",
        height: "var(--sz)",
        "--sz": "0.75rem",
        "--bg-open": "var(--background)"
      }}
    >
      <aside>
        <h6>Title</h6>
        <section>Content</section>
        <section>Content</section>
      </aside>
    </Polymorphic>
  );
}

Source Codes

Full working code example, including necessary markup and styles. You can copy and paste this code directly to start using the component immediately.

polymorphic-slot.tsx