Components
Overlay & Dialogs
Tooltip

Tooltip

Tooltip uses TooltipWrapper as the recommended abstraction. It bundles trigger, content, optional provider, and arrow controls in one component while preserving Radix behavior.

Installation

1. Install dependencies:

npm install @radix-ui/react-tooltip

2. Install the Tooltip primitive via shadcn:

npx shadcn@latest add tooltip

3. Copy the wrapper source:

4. Update import paths to match your project structure.


Usage

import { TooltipWrapper as Tooltip } from "@hoag/ui/tooltip-wrapper";
import { Button } from "@hoag/ui/components/button";
 
export default function Demo() {
  return (
    <Tooltip
      trigger={<Button variant="outline">Hover me</Button>}
      content="Helpful tooltip text"
    />
  );
}

Default

<Tooltip
  trigger={<Button variant="outline">Hover me</Button>}
  content="Helpful tooltip text"
/>

Placement and Arrow

<Tooltip
  trigger={<Button size="sm" variant="outline">Top</Button>}
  content="Top placement"
  contentProps={{ side: "top" }}
/>
 
<Tooltip
  trigger={<Button size="sm" variant="outline">No Arrow</Button>}
  content="Arrow hidden"
  showArrow={false}
  contentProps={{ side: "bottom" }}
/>

Trigger Modes

triggerAsChild (default)

Use when your trigger is already an interactive component like Button.

<Tooltip
  trigger={<Button variant="secondary">Hover for details</Button>}
  content="Uses your Button component as tooltip trigger"
/>

Native trigger element (triggerAsChild={false})

Use when you want wrapper to render trigger directly.

<Tooltip
  trigger="Native trigger"
  triggerAsChild={false}
  triggerClassName="inline-flex rounded-md border px-3 py-2 text-sm"
  content="Rendered by TooltipTrigger"
/>

Controlled Mode

Use controlled mode when tooltip state is managed by parent logic.

import { useState } from "react";
 
export default function ControlledTooltip() {
  const [open, setOpen] = useState(false);
 
  return (
    <div className="flex items-center gap-3">
      <Tooltip
        open={open}
        onOpenChange={setOpen}
        trigger={<Button variant="outline">Controlled</Button>}
        content={open ? "Tooltip is open" : "Tooltip is closed"}
      />
      <Button size="sm" variant="ghost" onClick={() => setOpen((v) => !v)}>
        Toggle programmatically
      </Button>
    </div>
  );
}

Provider Tuning

Use provider props to customize delay behavior globally per tooltip instance.

import { TooltipProvider } from "@hoag/ui/components/tooltip";
 
<Tooltip
  trigger={<Button size="sm" variant="outline">Fast</Button>}
  content="Short delay"
  providerProps={{ delayDuration: 100, skipDelayDuration: 100 }}
/>
 
<TooltipProvider delayDuration={150} skipDelayDuration={120}>
  <Tooltip
    trigger={<Button size="sm" variant="outline">External Provider</Button>}
    content="Uses parent provider"
    withProvider={false}
  />
</TooltipProvider>

If you set withProvider={false}, make sure a parent TooltipProvider exists.


API Reference

Props

PropTypeDefaultDescription
triggerReactNodeTooltip trigger element
triggerAsChildbooleantrueRender trigger as child
triggerClassNamestringClass override for trigger
contentReactNodeTooltip content
contentClassNamestringClass override for tooltip content
contentPropsOmit<ComponentProps<typeof TooltipContent>, "children" | "className">Extra content props (e.g. side, align, sideOffset)
showArrowbooleantrueShow/hide arrow
withProviderbooleantrueWrap with internal TooltipProvider
providerPropsOmit<ComponentProps<typeof TooltipProvider>, "children">Provider props (e.g. delayDuration, skipDelayDuration)
openbooleanControlled open state
defaultOpenbooleanfalseInitial open state
onOpenChange(open: boolean) => voidOpen state callback
delayDurationnumberDelay before opening (Tooltip root prop)
disableHoverableContentbooleanDisable hoverable content behavior

Content Positioning (contentProps)

Common props you can pass through contentProps:

  • side: "top" | "right" | "bottom" | "left"
  • align: "start" | "center" | "end"
  • sideOffset: number
  • alignOffset: number

Tips

  • Keep triggerAsChild enabled when using design-system buttons/links.
  • Use providerProps.delayDuration to tune hover sensitivity for dense UIs.
  • Use withProvider={false} if your layout already wraps a higher-level TooltipProvider.
  • Prefer short, descriptive content to keep tooltips scannable and accessible.

MIT 2026 © @hoag/ui