The complete tooltip, popover, dropdown, and menu solution for the web
—
Singleton system for creating shared tooltips that can be displayed across multiple reference elements with smooth transitions and centralized management.
Creates a single tooltip instance that can be shared across multiple reference elements, providing smooth transitions and consistent presentation.
/**
* Creates a singleton tooltip that can be shared across multiple instances
* @param tippyInstances - Array of existing tippy instances to control
* @param optionalProps - Optional properties for the singleton
* @returns Enhanced singleton instance with additional methods
*/
function createSingleton<TProps = Props>(
tippyInstances: Instance[],
optionalProps?: Partial<CreateSingletonProps<TProps>>
): CreateSingletonInstance<CreateSingletonProps<TProps>>;
type CreateSingletonProps<TProps = Props> = TProps & {
/** Properties that can be overridden by individual instances */
overrides: Array<keyof TProps>;
};
interface CreateSingletonInstance<TProps = CreateSingletonProps> extends Instance<TProps> {
/** Update the controlled instances */
setInstances(instances: Instance[]): void;
/** Show tooltip for specific target */
show(target?: ReferenceElement | Instance | number): void;
/** Show tooltip for next instance in sequence */
showNext(): void;
/** Show tooltip for previous instance in sequence */
showPrevious(): void;
}Usage Examples:
import tippy, { createSingleton } from "tippy.js";
// Create individual instances (initially disabled)
const instances = [
tippy('#button1', { content: 'Button 1 content' }),
tippy('#button2', { content: 'Button 2 content' }),
tippy('#button3', { content: 'Button 3 content' })
];
// Create singleton to control them
const singleton = createSingleton(instances, {
delay: 500,
placement: 'top',
moveTransition: 'transform 0.2s ease-out',
overrides: ['content', 'placement'] // Allow individual overrides
});
// Singleton automatically shows appropriate content when hovering over any buttonBuilt-in navigation methods for programmatically controlling which tooltip is displayed.
interface CreateSingletonInstance<TProps = CreateSingletonProps> {
/** Show tooltip for specific target by element, instance, or index */
show(target?: ReferenceElement | Instance | number): void;
/** Show tooltip for next instance in sequence */
showNext(): void;
/** Show tooltip for previous instance in sequence */
showPrevious(): void;
}Usage Examples:
import tippy, { createSingleton } from "tippy.js";
const instances = [
tippy('#step1', { content: 'Step 1: Introduction' }),
tippy('#step2', { content: 'Step 2: Configuration' }),
tippy('#step3', { content: 'Step 3: Completion' })
];
const singleton = createSingleton(instances, {
placement: 'bottom',
theme: 'tutorial'
});
// Programmatic navigation
function startTutorial() {
singleton.show(0); // Show first tooltip
}
function nextStep() {
singleton.showNext(); // Move to next tooltip
}
function previousStep() {
singleton.showPrevious(); // Move to previous tooltip
}
// Keyboard navigation
document.addEventListener('keydown', (e) => {
if (e.key === 'ArrowRight') nextStep();
if (e.key === 'ArrowLeft') previousStep();
if (e.key === 'Escape') singleton.hide();
});Dynamic management of controlled instances with the ability to add, remove, or replace instances.
interface CreateSingletonInstance<TProps = CreateSingletonProps> {
/** Replace all controlled instances with new set */
setInstances(instances: Instance[]): void;
}Usage Examples:
import tippy, { createSingleton } from "tippy.js";
// Initial setup
let instances = [
tippy('#item1', { content: 'Item 1' }),
tippy('#item2', { content: 'Item 2' })
];
const singleton = createSingleton(instances);
// Add new instances dynamically
function addTooltipTarget(element, content) {
const newInstance = tippy(element, { content });
instances.push(newInstance);
singleton.setInstances(instances); // Update singleton
}
// Remove instances
function removeTooltipTarget(element) {
instances = instances.filter(instance => instance.reference !== element);
singleton.setInstances(instances); // Update singleton
}
// Replace all instances
function updateAllTargets(newElements) {
instances = newElements.map(el => tippy(el, {
content: el.getAttribute('data-tooltip')
}));
singleton.setInstances(instances);
}Flexible override system allowing individual instances to customize specific properties while sharing common singleton behavior.
type CreateSingletonProps<TProps = Props> = TProps & {
/** Array of property names that can be overridden by individual instances */
overrides: Array<keyof TProps>;
};Usage Examples:
import tippy, { createSingleton } from "tippy.js";
// Create instances with different placements and content
const instances = [
tippy('#top-button', {
content: 'Top button',
placement: 'bottom' // Will override singleton
}),
tippy('#side-button', {
content: 'Side button',
placement: 'left' // Will override singleton
}),
tippy('#main-button', {
content: 'Main button'
// Will use singleton placement
})
];
const singleton = createSingleton(instances, {
delay: 300,
duration: 200,
placement: 'top', // Default placement
theme: 'custom',
overrides: ['content', 'placement'], // Allow these to be overridden
// delay, duration, theme cannot be overridden
});
// Individual instances can override content and placement
// but share delay, duration, and theme from singletonBuilt-in transition system for smooth movement between tooltip positions when switching targets.
// Singleton props for transition control
interface CreateSingletonProps {
/** CSS transition for smooth movement between positions */
moveTransition: string;
}Usage Examples:
import tippy, { createSingleton } from "tippy.js";
const instances = [
tippy('.nav-item', { content: 'Navigation item' })
];
// Custom transition effects
const smoothSingleton = createSingleton(instances, {
moveTransition: 'transform 0.3s cubic-bezier(0.4, 0, 0.2, 1)',
placement: 'bottom',
offset: [0, 8]
});
// Different transition for different contexts
const quickSingleton = createSingleton(instances, {
moveTransition: 'transform 0.1s ease-out', // Quick transitions
delay: 100
});
// No transition (instant movement)
const instantSingleton = createSingleton(instances, {
moveTransition: '', // Disable transitions
});Complex singleton usage patterns for specialized use cases.
Usage Examples:
import tippy, { createSingleton } from "tippy.js";
// Contextual help system
function createHelpSystem(containers) {
const allInstances = [];
containers.forEach(container => {
const helpElements = container.querySelectorAll('[data-help]');
const instances = Array.from(helpElements).map(el =>
tippy(el, {
content: el.dataset.help,
placement: el.dataset.placement || 'top'
})
);
allInstances.push(...instances);
});
return createSingleton(allInstances, {
theme: 'help-system',
delay: [600, 100],
moveTransition: 'transform 0.2s ease-out',
overrides: ['content', 'placement'],
interactive: true,
allowHTML: true
});
}
// Image gallery with captions
function createGalleryTooltips(gallery) {
const images = gallery.querySelectorAll('img[data-caption]');
const instances = Array.from(images).map(img =>
tippy(img, {
content: `
<div class="image-caption">
<h4>${img.alt}</h4>
<p>${img.dataset.caption}</p>
</div>
`,
allowHTML: true,
placement: 'bottom'
})
);
return createSingleton(instances, {
theme: 'gallery',
moveTransition: 'all 0.3s ease',
maxWidth: 300,
overrides: ['content']
});
}
// Keyboard-navigable tooltip tour
function createTooltipTour(steps) {
const instances = steps.map((step, index) =>
tippy(step.element, {
content: `
<div class="tour-step">
<div class="step-number">Step ${index + 1} of ${steps.length}</div>
<h3>${step.title}</h3>
<p>${step.description}</p>
</div>
`,
allowHTML: true,
placement: step.placement || 'top'
})
);
const tour = createSingleton(instances, {
theme: 'tour',
interactive: true,
hideOnClick: false,
trigger: 'manual',
overrides: ['content', 'placement']
});
// Add navigation controls
let currentStep = 0;
return {
start() {
currentStep = 0;
tour.show(0);
},
next() {
if (currentStep < instances.length - 1) {
currentStep++;
tour.show(currentStep);
}
},
previous() {
if (currentStep > 0) {
currentStep--;
tour.show(currentStep);
}
},
end() {
tour.hide();
}
};
}Install with Tessl CLI
npx tessl i tessl/npm-tippy-js