The complete tooltip, popover, dropdown, and menu solution for the web
—
Built-in plugin system with four official plugins for advanced behaviors and effects, plus a framework for creating custom plugins.
Extensible plugin architecture allowing custom behaviors and lifecycle hook modifications.
interface Plugin<TProps = Props> {
/** Optional plugin name for identification */
name?: string;
/** Default value when plugin property is not specified */
defaultValue?: any;
/** Plugin function that returns lifecycle hooks */
fn(instance: Instance<TProps>): Partial<LifecycleHooks<TProps>>;
}
// Usage in props
interface Props {
/** Array of plugins to apply to the instance */
plugins: Plugin[];
}Usage Examples:
import tippy, { animateFill, followCursor } from "tippy.js";
// Using built-in plugins
tippy('.animated', {
content: 'Animated tooltip',
plugins: [animateFill, followCursor],
animateFill: true,
followCursor: true
});
// Custom plugin example
const customPlugin = {
name: 'customBehavior',
defaultValue: false,
fn(instance) {
return {
onShow() {
console.log('Custom plugin: tooltip showing');
},
onHide() {
console.log('Custom plugin: tooltip hiding');
}
};
}
};
tippy('#custom', {
content: 'Custom plugin tooltip',
plugins: [customPlugin]
});Creates a backdrop fill animation effect that fills the tooltip background before showing content.
interface AnimateFill extends Plugin {
name: 'animateFill';
defaultValue: false;
}
const animateFill: AnimateFill;
// Props extension
interface Props {
/** Enable animate fill effect */
animateFill: boolean;
}Usage Examples:
import tippy, { animateFill } from "tippy.js";
// Basic animate fill
tippy('.fill-animation', {
content: 'Filling tooltip',
plugins: [animateFill],
animateFill: true,
animation: 'shift-away' // Required for proper effect
});
// Animate fill with custom styling
tippy('.custom-fill', {
content: 'Custom fill effect',
plugins: [animateFill],
animateFill: true,
theme: 'custom-fill', // Style the backdrop in CSS
duration: 500
});
// Note: animateFill requires the default render function
// It automatically sets arrow: false and animation: 'shift-away'Makes the tooltip follow the mouse cursor with various tracking modes.
interface FollowCursor extends Plugin {
name: 'followCursor';
defaultValue: false;
}
const followCursor: FollowCursor;
// Props extension
interface Props {
/** Follow cursor behavior */
followCursor: boolean | 'horizontal' | 'vertical' | 'initial';
}Usage Examples:
import tippy, { followCursor } from "tippy.js";
// Full cursor following
tippy('.follow-cursor', {
content: 'Follows cursor',
plugins: [followCursor],
followCursor: true
});
// Horizontal only
tippy('.follow-horizontal', {
content: 'Follows horizontally',
plugins: [followCursor],
followCursor: 'horizontal'
});
// Vertical only
tippy('.follow-vertical', {
content: 'Follows vertically',
plugins: [followCursor],
followCursor: 'vertical'
});
// Initial position then fixed
tippy('.follow-initial', {
content: 'Initial position only',
plugins: [followCursor],
followCursor: 'initial' // Follows until shown, then stays
});
// Follow cursor with boundaries
tippy('.bounded-follow', {
content: 'Bounded following',
plugins: [followCursor],
followCursor: true,
boundary: 'viewport' // Respect viewport boundaries
});Improves positioning for inline elements like text selections and inline spans.
interface InlinePositioning extends Plugin {
name: 'inlinePositioning';
defaultValue: false;
}
const inlinePositioning: InlinePositioning;
// Props extension
interface Props {
/** Enable inline positioning improvements */
inlinePositioning: boolean;
}Usage Examples:
import tippy, { inlinePositioning } from "tippy.js";
// Better positioning for inline elements
tippy('span.inline-tooltip', {
content: 'Inline element tooltip',
plugins: [inlinePositioning],
inlinePositioning: true,
placement: 'top'
});
// Text selection tooltips
tippy('.text-content', {
content: 'Text selection tooltip',
plugins: [inlinePositioning],
inlinePositioning: true,
trigger: 'manual', // Control manually
getReferenceClientRect() {
const selection = window.getSelection();
return selection.getRangeAt(0).getBoundingClientRect();
}
});
// Inline code elements
tippy('code', {
content: 'Code documentation',
plugins: [inlinePositioning],
inlinePositioning: true,
placement: 'bottom',
delay: 500
});Keeps the tooltip positioned relative to its reference element during scrolling or element movement.
interface Sticky extends Plugin {
name: 'sticky';
defaultValue: false;
}
const sticky: Sticky;
// Props extension
interface Props {
/** Sticky behavior */
sticky: boolean | 'reference' | 'popper';
}Usage Examples:
import tippy, { sticky } from "tippy.js";
// Stick to both reference and popper
tippy('.sticky-tooltip', {
content: 'Sticky tooltip',
plugins: [sticky],
sticky: true // Monitor both reference and popper positions
});
// Stick only to reference element
tippy('.sticky-reference', {
content: 'Sticks to reference',
plugins: [sticky],
sticky: 'reference' // Only monitor reference element
});
// Stick only to popper element
tippy('.sticky-popper', {
content: 'Sticks to popper',
plugins: [sticky],
sticky: 'popper' // Only monitor popper element
});
// Sticky with scrollable containers
tippy('.scrollable-content .tooltip-trigger', {
content: 'Scrollable sticky tooltip',
plugins: [sticky],
sticky: true,
boundary: 'scrollParent' // Respect scroll boundaries
});Using multiple plugins together for complex behaviors.
Usage Examples:
import tippy, { followCursor, sticky, inlinePositioning } from "tippy.js";
// Follow cursor with sticky behavior
tippy('.advanced-tooltip', {
content: 'Advanced behavior',
plugins: [followCursor, sticky],
followCursor: 'initial', // Initial follow, then stick
sticky: true,
duration: 200
});
// Inline positioning with animate fill
tippy('span.highlighted', {
content: 'Highlighted text tooltip',
plugins: [inlinePositioning, animateFill],
inlinePositioning: true,
animateFill: true,
theme: 'highlight'
});
// All plugins combined
tippy('.kitchen-sink', {
content: 'All features tooltip',
plugins: [followCursor, sticky, inlinePositioning, animateFill],
followCursor: 'horizontal',
sticky: 'reference',
inlinePositioning: true,
animateFill: true,
interactive: true
});Framework for creating custom plugins with lifecycle hooks.
Usage Examples:
import tippy from "tippy.js";
// Simple logging plugin
const loggingPlugin = {
name: 'logging',
defaultValue: false,
fn(instance) {
return {
onCreate() {
console.log(`Tooltip created for:`, instance.reference);
},
onShow() {
console.log(`Tooltip showing:`, instance.props.content);
},
onHide() {
console.log(`Tooltip hiding:`, instance.reference);
}
};
}
};
// Analytics plugin
const analyticsPlugin = {
name: 'analytics',
defaultValue: false,
fn(instance) {
return {
onShow() {
if (instance.props.analytics) {
analytics.track('tooltip_shown', {
content: instance.props.content,
element: instance.reference.tagName
});
}
}
};
}
};
// Auto-hide plugin
const autoHidePlugin = {
name: 'autoHide',
defaultValue: 0,
fn(instance) {
let timeoutId;
return {
onShow() {
if (instance.props.autoHide > 0) {
timeoutId = setTimeout(() => {
instance.hide();
}, instance.props.autoHide);
}
},
onHide() {
if (timeoutId) {
clearTimeout(timeoutId);
}
}
};
}
};
// Usage of custom plugins
tippy('.custom-behavior', {
content: 'Custom plugin tooltip',
plugins: [loggingPlugin, analyticsPlugin, autoHidePlugin],
analytics: true,
autoHide: 3000 // Auto-hide after 3 seconds
});
// Plugin with state management
const counterPlugin = {
name: 'counter',
defaultValue: false,
fn(instance) {
let showCount = 0;
return {
onShow() {
if (instance.props.counter) {
showCount++;
instance.setContent(`Shown ${showCount} times`);
}
}
};
}
};Install with Tessl CLI
npx tessl i tessl/npm-tippy-js