How to Create Custom Tooltips and Popovers Using Element UI Components

You create custom tooltips and popovers in Element UI by leveraging the shared Vue-popper mixin, applying CSS classes via the popperClass prop, and controlling visibility through trigger configurations or manual v-model bindings.

Element UI provides robust Tooltip and Popover components for displaying contextual information in Vue.js applications. Both components are thin wrappers around the vue-popper utility (element-ui/src/utils/vue-popper), sharing the same positioning engine while offering distinct APIs for simple text hints versus rich content panels. This guide demonstrates how to create custom tooltips and popovers by examining the source code architecture from the ElemeFE/element repository.

Understanding the Shared Vue-Popper Architecture

Both Tooltip and Popover components import the Popper mixin from element-ui/src/utils/vue-popper, which injects core positioning logic, the updatePopper() method, and the reactive showPopper flag that drives visibility transitions.

Aspect Tooltip Popover
Core file packages/tooltip/src/main.js packages/popover/src/main.vue
Render strategy Render function building <transition><div role="tooltip"> Template rendering <transition><div role="tooltip" class="el-popover">
Reference detection getFirstElement() extracts first child from default slot reference prop, named reference slot, or first child
Default triggers mouseenter / mouseleave Configurable via trigger prop: click, hover, focus, manual

According to the ElemeFE/element source code, Tooltip implements event listeners in its mounted() hook (lines 14-30), while Popover attaches listeners based on the trigger configuration in its own mounted() method (lines 17-36).

Customizing Appearance with CSS Classes and Transitions

Both components expose popperClass and transition props to customize the popper element's styling and animation. In Tooltip, popperClass is applied alongside el-tooltip__popper and effect classes in the render logic (line 27), while Popover accepts the same prop to append classes to its root element (line 55).

<el-tooltip content="Styled tooltip" popper-class="my-custom-tooltip" placement="bottom">
  <el-button>Custom Styled</el-button>
</el-tooltip>

<style>
.my-custom-tooltip {
  background: #333;
  color: #fff;
  border-radius: 4px;
}
</style>

The transition prop (Tooltip lines 32-34, Popover lines 64-66) accepts animation names to customize the show/hide behavior, as both components wrap content in <transition> elements.

Creating Rich HTML Content

Tooltip supports multi-line HTML through the content slot, overriding the content prop to allow arbitrary markup:

<el-tooltip placement="top" effect="light">
  <div slot="content">
    Multi-line information<br/>
    Second line of text
  </div>
  <el-button>Hover me</el-button>
</el-tooltip>

Popover extends this capability with a dedicated title prop and structured slots for complex layouts:

<el-popover trigger="click" title="提示标题" width="200" placement="right" v-model="show">
  <p>Arbitrary HTML content can go here.</p>
  <el-button slot="reference">Open Popover</el-button>
</el-popover>

The reference slot explicitly defines the trigger element, offering more flexibility than Tooltip's automatic first-element detection via getFirstElement() (lines 14-24 in main.js).

Controlling Visibility and Triggers

Manual control disables automatic event listeners, allowing programmatic management via the showPopper internal state:

<template>
  <el-tooltip :manual="true" v-model="visible" content="Controlled tooltip">
    <el-button @click="visible = !visible">Toggle tooltip</el-button>
  </el-tooltip>
</template>

<script>
export default {
  data() {
    return { visible: false };
  }
};
</script>

Trigger delays prevent accidental activation using openDelay and closeDelay props. In Popover, these control timers within handleMouseEnter and handleMouseLeave methods (lines 68-90 in main.vue):

<el-popover trigger="hover" open-delay="300" close-delay="200" v-model="visible" placement="bottom">
  <p>Hover-delayed content</p>
  <el-button slot="reference">Hover me</el-button>
</el-popover>

For click-triggered popovers, the component automatically handles outside clicks via handleDocumentClick to close the popover when clicking elsewhere.

Working with Reference Elements

Tooltip automatically detects its reference element by extracting the first element from the default slot using getFirstElement(). Popover provides three methods for reference specification:

  1. reference prop: Pass a DOM element or selector
  2. reference slot: Explicitly define the trigger element in the template
  3. Default slot: First child element (fallback behavior)

This architecture is initialized in Popover's mounted() hook (lines 88-99 in main.vue), ensuring reliable event listener attachment regardless of how the reference is specified.

Accessibility Implementation

Both components implement ARIA best practices according to the source code. Tooltip adds aria-describedby and tabindex attributes to reference elements, while Popover adds the same attributes plus tabindex="0" on the popover element itself, ensuring full keyboard navigability and screen reader compatibility.

Summary

  • Shared foundation: Both components use the Popper mixin from src/utils/vue-popper.js for positioning and the showPopper reactive flag.
  • Styling customization: Apply the popperClass prop to inject custom CSS classes into the generated popper DOM element.
  • Content flexibility: Use the content slot in Tooltip for HTML, and the title prop plus default slot in Popover for structured panels.
  • Visibility control: Configure trigger types in Popover, or use manual mode with v-model in both components for programmatic control.
  • Timing control: Implement openDelay and closeDelay to prevent accidental triggering, managed internally by mouse event handlers.
  • Reference handling: Use explicit reference slots in Popover for complex layouts, or rely on Tooltip's automatic first-element detection.

Frequently Asked Questions

How do I apply custom CSS to a tooltip or popover?

Use the popper-class (or popperClass) prop to add custom CSS classes to the popper element. This class is appended to the default classes (el-tooltip__popper or el-popover) in the rendered output, allowing you to override background colors, borders, and shadows as implemented in packages/tooltip/src/main.js and packages/popover/src/main.vue.

What is the difference between Tooltip and Popover in Element UI?

Tooltip is optimized for simple text hints, automatically detecting its trigger from the default slot and defaulting to hover behavior. Popover functions as an enhanced Tooltip with support for titles, explicit reference slots, multiple trigger types (click, hover, focus), and width constraints. Both share the same underlying positioning logic from src/utils/vue-popper.js, but Popover offers greater flexibility for interactive content panels.

How can I programmatically control tooltip visibility?

Set the manual prop to true and bind the v-model directive to a boolean data property. This disables the default mouse listeners defined in the mounted() hook (lines 14-30) and allows direct toggling of the internal showPopper state, giving you complete control over visibility timing.

Why does my popover close immediately after opening?

Ensure you are not binding the same v-model variable to multiple popovers or conflicting trigger conditions. When using trigger="click", the component uses handleDocumentClick to detect outside clicks; verify that your click target is not being detached or that you are not programmatically setting visibility to false in conflicting event handlers.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →