Nesting-free Tippy.js components for Vue 3
There's already a popular Vue library for using Tippy.js, VueTippy, so you might (rightfully) ask, why make another one?
To put it simply, VueTippy makes a few structural concessions which I disagree with. The most significant is that when
using VueTippy, adding a complex (i.e. non-directive) tooltip to an element will wrap it in a <span>
, which can easily
screw up layout CSS. On top of that, I find their syntax clunky and ugly. Wrapping every tooltipped component in a slot
seems unnecessary.
A clumsy tool will wind up chronically underutilized, so Tippy.vue has been designed from the start with a strong focus on ergonomics. Adding a tooltip is a simple, drop-in addition, with no structural or styling changes necessary.
<!-- VueTippy -->
<tippy>
<button>Tippy!</button>
<template #content>
Hi!
</template>
</tippy>
<!-- Tippy.vue -->
<button v-tippy>Tippy!</button>
<tippy>Hi!</tippy>
# npm
npm i tippy.vue
# Yarn
yarn add tippy.vue
Tippy.vue doesn't bundle Tippy.js. The most up-to-date Tippy install process is explained in the Tippy docs, but as of the time of writing, these are the necessary scripts:
<!-- Development -->
<script src="https://unpkg.com/@popperjs/core@2/dist/umd/popper.min.js"></script>
<script src="https://unpkg.com/tippy.js@6/dist/tippy-bundle.umd.js"></script>
<script src="https://unpkg.com/tippy.vue@3"></script>
<!-- Production -->
<script src="https://unpkg.com/@popperjs/core@2"></script>
<script src="https://unpkg.com/tippy.js@6"></script>
<script src="https://unpkg.com/tippy.vue@3"></script>
You can use Tippy.vue as a plugin or access the individual components directly:
// use the plugin
import {TippyPlugin} from 'tippy.vue';
app.use(TippyPlugin);
app.use(TippyPlugin, {
// convenience to set tippy.js default props
});
// or add them individually
import {TippyDirective, Tippy, TippySingleton} from 'tippy.vue';
app.directive('tippy', TippyDirective);
app.component('tippy', Tippy);
app.component('tippy-singleton', TippySingleton);
import tippy from 'tippy.js'
tippy.setDefaultProps({
// default tippy props
});
/* add styles/themes to your global stylesheet */
@import '~tippy.js/dist/tippy.css';
You can also add them in individual components:
<template>
<div>
<div v-tippy>Wow</div>
<tippy>Cool</tippy>
</div>
</template>
<script>
import {Tippy, TippyDirective} from 'tippy.vue'
export default {
components: {
Tippy
},
directives: {
tippy: TippyDirective
}
}
</script>
// use the plugin
app.use(TippyVue);
app.use(TippyVue, {
// convenience to set tippy.js default props
});
// or add them individually
app.directive('tippy', TippyVue.TippyDirective);
app.component('tippy', TippyVue.Tippy);
app.component('tippy-singleton', TippyVue.TippySingleton);
tippy.setDefaultProps({
// default tippy props
});
<button v-tippy="'Some content'">Static</button>
<button v-tippy="`Seconds: ${seconds}`">Counter</button>
<button v-tippy="{content: 'Some content', position: 'right'}">Side</button>
<!-- Target arbitrary elements -->
<button v-tippy>Basic</button>
<tippy>Time: <i>{{seconds}}</i></tippy>
<!-- Named targets -->
<button v-tippy:fancy>Fancy</button>
<tippy target="fancy">Fancy time: <i>{{seconds}}</i></tippy>
<!-- Target the parent -->
<button>
Button
<tippy target="_parent">Bound to parent</tippy>
</button>
<!-- Commonly used options have dedicated props -->
<button v-tippy></button>
<tippy placement="right">On the right</tippy>
<!-- More niche options can be passed into the 'extra' prop -->
<button v-tippy>Follow me</button>
<tippy :extra="{followCursor: true}">Tip</tippy>
<tippy-singleton move-transition="transform 0.1s ease-out"/>
<button v-tippy>1</button>
<tippy singleton>Item 1</tippy>
<button v-tippy>2</button>
<tippy singleton>Item 2</tippy>
<button v-tippy>3</button>
<tippy singleton>Item 3</tippy>