GitHub

Example usages

Tooltip

Let's create a Tooltip component.

<Tooltip
placement="top"
label="Is tech making coffee better or worse?">
<button className="rounded-md bg-white px-8 py-4 text-sm font-semibold text-slate-900 shadow">
Tooltip Trigger
</button>
</Tooltip>

1. Quick Way - using AttachedOverlay component

import React from 'react';
import type { Variant } from 'framer-motion';
import clsx from 'clsx';
import { IAttachedOverlayProps, AttachedOverlay } from 'react-useoverlay';
// predefined color props for the tooltip
type color = 'primary' | 'secondary' | 'success' | 'error' | 'warning' | 'info';
interface ITooltipTheme {
initial: string[];
animations: { open: Variant; close: Variant };
color: Record<color, string[]>;
}
// I'm using Tailwind for theming of the tooltip.
// But you are free to use any other solutions.
const styles: ITooltipTheme = {
initial: ['text-white font-medium rounded shadow-sm px-3 py-1.5'],
animations: {
open: {
opacity: 1,
transform: 'scale(1)',
transition: { duration: 0.15, ease: [0.165, 0.84, 0.44, 1] },
},
close: {
opacity: 0,
transform: 'scale(0)',
transition: { duration: 0.15, ease: [0.165, 0.84, 0.44, 1] },
},
},
color: {
primary: ['bg-indigo-500'],
secondary: ['bg-slate-500'],
success: ['bg-green-500'],
error: ['bg-red-500'],
warning: ['bg-amber-500'],
info: ['bg-sky-500'],
},
};
// component prop interface
// we are omiting `overlay` & `children` props because we are gonna handle it by ourself
export interface MyCustomTooltipProps
extends Omit<IAttachedOverlayProps, 'overlay' | 'children'> {
label: string; // you can also accept ReactNode etc
className?: string;
color?: color;
children: JSX.Element;
}
// actual component
export const Tooltip: React.FC<MyCustomTooltipProps> = ({
// define your defaults for the tooltip
placement = 'top',
interactive = true,
useMotion = true,
offset = 8,
shift = 8,
delay = { open: 0, close: 0 },
use = { hover: true, focus: true },
usePortal = true,
// your custom props
color = 'primary',
label,
children,
className,
...rest
}) => {
const classes = clsx(styles.initial, styles.color[color], className);
return (
<AttachedOverlay
{...{
interactive,
placement,
useMotion,
offset,
shift,
delay,
use,
usePortal,
}}
overlay={() => <div className={classes}>{label}</div>}
{...rest}
>
{/* AttachedOverlay expects function as a child */}
{/* we are gonna need it later for other components */}
{() => children}
</AttachedOverlay>
);
};

2. Custom Way - using useOverlay hook

import React, { cloneElement } from 'react';
import type { Placement } from '@floating-ui/react-dom-interactions';
import type { Variant } from 'framer-motion';
import clsx from 'clsx';
import { IOverlay, useOverlay } from 'react-useoverlay';
// predefined color props for the tooltip
type color = 'primary' | 'secondary' | 'success' | 'error' | 'warning' | 'info';
export interface ITooltipTheme {
initial: string[];
animations: { open: Variant; close: Variant };
color: Record<color, string[]>;
}
// I'm using Tailwind for theming of the tooltip.
// But you are free to use any other solutions.
export const styles: ITooltipTheme = {
initial: [
'text-white font-medium rounded shadow-sm px-3 py-1.5',
],
animations: {
open: {
opacity: 1,
transform: 'scale(1)',
transition: { duration: 0.15, ease: [0.165, 0.84, 0.44, 1] },
},
close: {
opacity: 0,
transform: 'scale(0)',
transition: { duration: 0.15, ease: [0.165, 0.84, 0.44, 1] },
},
},
color: {
primary: ['bg-indigo-500'],
secondary: ['bg-slate-500'],
success: ['bg-green-500'],
error: ['bg-red-500'],
warning: ['bg-amber-500'],
info: ['bg-sky-500'],
},
};
// component prop interface
export interface ITooltipProps
extends Pick<
IOverlay,
| 'delay'
| 'useMotion'
| 'interactive'
| 'offset'
| 'shift'
| 'usePortal'
| 'className'
> {
label: string;
placement?: Placement;
color?: color;
children: JSX.Element;
}
// actual component
export const Tooltip: React.FC<ITooltipProps> = ({
interactive = true,
useMotion = true,
placement = 'top',
color = 'primary',
offset = 8,
shift = 8,
delay = { open: 0, close: 0 },
usePortal = true,
label,
children,
className,
}) => {
const classes = clsx(styles.initial, styles.color[color], className );
// we are passing almost all props to useOverlay hook
const { triggerProps, overlay } = useOverlay({
// we are checking this to disable the tooltip triggers
interactive,
// we are wrapping with AnimatePresence to make it motion compatible
useMotion,
// start of floating-ui props
placement,
role: 'tooltip',
use: { click: false, dismiss: true, focus: true, hover: true },
usePortal,
offset,
shift,
delay,
animations: styles.animations, // framer motion
// here you can pass your floating element
// this overlay function gives you the close function
// so you can use it in your component
// we will see it in action in the next examples live modal & drawer
// for tooltip we don't need to close element manually
// because we are only using useHover and useFocus triggers
overlay: () => <div className={classes}>{label}</div>,
});
// we need to wrap string children in a span to make them interactive
return (
<>
{typeof children === 'string' ? (
<span {...triggerProps}>{children}</span>
) : (
// if children is not a string, we can just clone it
cloneElement(children, triggerProps)
)}
{/* this is the actual overlay */}
{cloneElement(overlay)}
</>
);
};

What is React.cloneElement?

Previous
Installation