Skip to content

Slot

Merges its own props onto its single child element instead of rendering a wrapper — the asChild escape hatch, so <Button asChild><Link/></Button> yields one <a> carrying the button's styling and behaviour.

import { Slot } from "@evolve-storefront/ui/helpers/slot";
Design notes

Merge order matches the convention asChild consumers already expect: the child wins plain props, event handlers compose (the child's runs first, so it can preventDefault before ours sees the event), style merges key-wise with the child winning, and className goes through cn() so a consumer's utility beats a conflicting variant class rather than depending on source order the way a plain join would.

Preview

DefaultOpen in Storybook ↗

Usage

One <button>, carrying the slot's classes and its click handler.

function Tag({ asChild = false, ...props }: TagProps) {
const Component = asChild ? Slot : "span";
return <Component data-component="tag" className="rounded px-2" {...props} />;
}
<Tag asChild><Link href="/sale">{t("sale")}</Link></Tag>;

Props

PropTypeDescription
children?ReactNode
ref?Ref<unknown>