Important: This documentation covers Yarn 1 (Classic).
For Yarn 2+ docs and migration guide, see yarnpkg.com.

Package detail

@ngneat/helipopper

ngneat63kMIT10.2.1TypeScript support: included

A Powerful Tooltip and Popover for Angular Applications

angular, angular tooltip, angular popover, tooltip, popover, tippy.js, popper.js, helipopper

readme


MIT commitizen PRs styled with prettier All Contributors ngneat spectator @ngneat/helipopper

A Powerful Tooltip and Popover for Angular Applications

Tippy.js is the complete tooltip, popover, dropdown, and menu solution for the web, powered by Popper.js.

It is an abstraction over Popper that provides the logic and optionally the styling involved in all types of elements that pop out from the flow of the document and get overlaid on top of the UI, positioned next to a reference element.

This is a lightweight wrapper with additional features that lets you use it declaratively in Angular. Tippy has virtually no restrictions over Popper and gives you limitless control while providing useful behavior and defaults.

If you're using v1 and don't want to migrate, you can find it here.

Features

✅ Position Tooltips, Menus, Dropdowns, and Popovers
✅ Predefined Variations
✅ TemplateRef/Component Support
✅ Lazy Registration
✅ Manual Trigger Support
✅ Text Overflow Support
✅ Context Menu Support

Installation

$ npm i @ngneat/helipopper
# Or if you're using yarn
$ yarn add @ngneat/helipopper
# Or if you're using pnpm
$ pnpm i @ngneat/helipopper

Configure it as shown below:

import { provideTippyLoader, provideTippyConfig, tooltipVariation, popperVariation } from '@ngneat/helipopper/config';

bootstrapApplication(AppComponent, {
  providers: [
    provideTippyLoader(() => import('tippy.js')),
    provideTippyConfig({
      defaultVariation: 'tooltip',
      variations: {
        tooltip: tooltipVariation,
        popper: popperVariation,
      },
    }),
  ],
});

Please note that the provideTippyLoader is required, as it specifies how Tippy is loaded - either synchronously or asynchronously. When dynamic import is used, the library will load only when the first Tippy directive is rendered. If we want it to load synchronously, we use the following:

import tippy from 'tippy.js';

provideTippyLoader(() => tippy);

Add the styles you want to styles.scss:

@import 'tippy.js/dist/tippy.css';
@import 'tippy.js/themes/light.css';
@import 'tippy.js/animations/scale.css';

You have the freedom to customize it if you need to.

Import the standalone TippyDirective in your components:

import { TippyDirective } from '@ngneat/helipopper';

@Component({
  standalone: true,
  imports: [TippyDirective],
})
class ExampleComponent {}

And use it in your templates:

<button tp="Helpful Message">I have a tooltip</button>

The library exposes default variations for tooltip and popper. You can use them, extend them, or pass your own variations. A variation is a set of predefined tippy properties. For example, here's how the built-in tooltip variation looks like:

export const tooltipVariation = {
  theme: null,
  arrow: false,
  animation: 'scale',
  trigger: 'mouseenter',
  offset: [0, 5],
};

Use TemplateRef as content

<button [tp]="tpl" tpVariation="popper">Click Me</button>

<ng-template #tpl let-hide>
  <h6>Popover title</h6>
  <p>And here's some amazing content. It's very engaging. Right?</p>
</ng-template>

Use Component as content

import type { TippyInstance } from '@ngneat/helipopper/config';
import { injectTippyRef } from '@ngneat/helipopper';

@Component()
class MyComponent {
  tippy = injectTippyRef();
}
<button [tp]="MyComponent">Click Me</button>

Text Overflow

You can pass the onlyTextOverflow input to show the tooltip only when the host overflows its container:

<div style="max-width: 100px;" class="overflow-hidden flex">
  <p class="ellipsis" [tp]="text" tpPlacement="right" [tpOnlyTextOverflow]="true">
    {{ text }}
  </p>
</div>

Note: this feature is using ResizeObserver api.

You might have cases where the host has a static width and the content is dynamic. In this case, use the tpStaticWidthHost input with combination with tpOnlyTextOverflow:

<div style="max-width: 100px;" class="overflow-hidden flex">
  <p
    style="width: 100px"
    class="ellipsis"
    [tp]="dynamicText"
    tpPlacement="right"
    [tpOnlyTextOverflow]="true"
    tpStaticWidthHost
  >
    {{ dynamicText }}
  </p>
</div>

Note: when using tpStaticWidthHost you can't use tpUseTextContent, you need to explicitly pass the content to tp in order to trigger content change.

Use Text Content

You can instruct tippy to use the element textContent as the tooltip content:

<p tp tpUseTextContent>{{ text }}</p>

Lazy

You can pass the tpIsLazy input when you want to defer the creation of tippy only when the element is in the view:

<div *ngFor="let item of items" [tp]="item.label" [tpIsLazy]="true">{{ item.label }}</div>

Note that it's using IntersectionObserver api.

Context Menu

First, define the contextMenu variation:

import {
  popperVariation,
  tooltipVariation,
  provideTippyConfig,
  withContextMenuVariation,
} from '@ngneat/helipopper/config';

bootstrapApplication(AppComponent, {
  providers: [
    provideTippyConfig({
      defaultVariation: 'tooltip',
      variations: {
        tooltip: tooltipVariation,
        popper: popperVariation,
        contextMenu: withContextMenuVariation(popperVariation),
      },
    }),
  ],
});

Now you can use it in your template:

<ng-template #contextMenu let-hide let-item="data">
  <ul>
    <li (click)="copy(item); hide()">Copy</li>
    <li (click)="duplicate(item); hide()">Duplicate</li>
  </ul>
</ng-template>

<ul>
  <li
    *ngFor="let item of list"
    [tp]="contextMenu"
    [tpData]="item"
    tpVariation="contextMenu"
  >
    {{ item.label }}
  </li>
</ul>

Manual Trigger

<div tp="Helpful Message" tpTrigger="manual" #tooltip="tippy">Click Open to see me</div>

<button (click)="tooltip.show()">Open</button>
<button (click)="tooltip.hide()">Close</button>

Declarative show/hide

Use isVisible to trigger show and hide. Set trigger to manual.

<div tp="Helpful Message" tpTrigger="manual" [tpIsVisible]="visibility">
  Click Open to see me
</div>

<button (click)="visibility = true">Open</button>
<button (click)="visibility = false">Close</button>

You can see more examples in our playground, or live here.

Inputs

tp: string | TemplateRef<any> | Type<any> | undefined | null;
tpAppendTo: TippyProps['appendTo'];
tpDelay: TippyProps['delay'];
tpDuration: TippyProps['duration'];
tpHideOnClick: TippyProps['hideOnClick'];
tpInteractive: TippyProps['interactive'];
tpInteractiveBorder: TippyProps['interactiveBorder'];
tpMaxWidth: TippyProps['maxWidth'];
tpOffset: TippyProps['offset'];
tpPlacement: TippyProps['placement'];
tpPopperOptions: TippyProps['popperOptions'];
tpShowOnCreate: TippyProps['showOnCreate'];
tpTrigger: TippyProps['trigger'];
tpTriggerTarget: TippyProps['triggerTarget'];
tpZIndex: TippyProps['zIndex'];
tpAnimation: TippyProps['animation'];
tpUseTextContent: boolean;
tpIsLazy: boolean;
tpVariation: string;
tpIsEnabled: boolean;
tpIsVisible: boolean;
tpClassName: string;
tpOnlyTextOverflow: boolean;
tpData: any;
tpUseHostWidth: boolean;
tpHideOnEscape: boolean;
tpDetectChangesComponent: boolean;
tpPopperWidth: number | string;
tpHost: HTMLElement;
tpIsVisible: boolean;

Outputs

tpVisible = new EventEmitter<boolean>();

Global Config

  • You can pass any tippy option at global config level.
  • beforeRender - Hook that'll be called before rendering the tooltip content ( applies only for string )

Create tippy Programmatically

import type { TippyInstance } from '@ngneat/helipopper/config';
import { TippyService } from '@ngneat/helipopper';

class Component {
  @ViewChild('inputName') inputName: ElementRef;
  tippy: TippyInstance;
  private tippyService = inject(TippyService);

  async show() {
    if (!this.tippy) {
      this.tippy = await firstValueFrom(
        this.tippyService.create(this.inputName, 'this field is required')
      );
    }

    this.tippy.show();
  }

  ngOnDestroy() {
    this.tippy?.destroy();
  }
}

Contributors ✨

Thank goes to all these wonderful people who contributed ❤️

changelog

Changelog

All notable changes to this project will be documented in this file. See standard-version for commit guidelines.

10.2.1 (2025-05-27)

Bug Fixes

  • onHide emit only if ref is not destroyed (300c4f0)

10.2.0 (2025-05-25)

Features

  • add @ngneat/helipopper/config (fe59df7)
  • allow lazy-loading tippy.js (3913322)
  • expose onShow and onHide from tippyJS (05ef2ee)
  • switch to signal inputs (a7b6512)
  • upgrade to Angular 19 (d33b7b2)

Bug Fixes

  • do not update isVisible signal when view is destroyed (7c43af9)
  • enable strict mode (419bf97)
  • watch isEnabled changes (c8e1a4f)

10.2.0-alpha.2(2025-01-03)

Refactor

  • do not export config entry and do some cleanups (aed43fc)

10.2.0-alpha.1(2025-01-01)

Refactor

  • tree-shake injection token names (ff51f57)

10.2.0-alpha.0(2024-12-07)

Features

Fixes

  • do not update isVisible signal when view is destroyed (7c43af9)

10.1.0-alpha.0(2024-11-20)

Features

  • add @ngneat/helipopper/config (fe59df7)

10.0.0-alpha.1 (2024-11-17)

Fixes

10.0.0-alpha.0 (2024-11-17)

⚠ BREAKING CHANGES

  • tippy.js must be explicitly passed to the loader function when calling provideTippyConfig (see README).

Features

  • allow lazy-loading tippy.js (74a3ed6)
  • switch to signal inputs (e698cca)

9.2.1 (2023-12-13)

Bug Fixes

  • 🐛 allow injector also in service creation (cb3e6ef)

9.2.0 (2023-12-13)

Features

  • 🎸 introduce injectTippyRef (129b91a)

9.1.0 (2023-12-11)

Features

  • 🎸 use transform on boolean inputs (0eec969)

9.0.0 (2023-11-28)

⚠ BREAKING CHANGES

  • peer deps of the library and overview are v17

Features

  • 🎸 update to ng17 (65f4c76)
  • 🎸 use makeEnvironmentProviders for config (e629cb2)

8.0.3 (2023-09-05)

Bug Fixes

  • 🐛 handle content changes with overflow (ef3264b)

8.0.2 (2023-07-24)

Bug Fixes

  • 🐛 move deps from peer to deps (05579e0), closes #143

8.0.1 (2023-07-16)

Bug Fixes

8.0.0 (2023-07-09)

⚠ BREAKING CHANGES

  • peer dependency is now angular v16+

Features

  • 🎸 support host visibility observer (5907e34)
  • 🤖 update to angular v16 (04e9fbb)

7.1.1 (2023-04-24)

Bug Fixes

7.1.0 (2023-04-24)

Features

  • 🎸 add global get index (2c29cd4)

7.0.2 (2023-03-15)

Bug Fixes

  • 🐛 remove tippy warning when using isLazy (33cb300), closes #134

7.0.1 (2023-03-09)

Bug Fixes

  • 🐛 template type checking (66a0f82)

7.0.0 (2023-03-09)

⚠ BREAKING CHANGES

  • Rename tippy to tp
  • Prefix all inputs with tp
  • Rename lazy input to tpIsLazy

You can find a script that will help you migrate here.

refactor

  • 💡 Rename directive inputs (cc1a5a6)

6.6.0 (2023-02-26)

Features

  • make tippy directive extendable (e94a27c)

Bug Fixes

  • do not trigger change detection for visible and keydown events (39c69c3)

6.5.2 (2023-02-04)

6.5.1 (2023-01-28)

Bug Fixes

6.5.0 (2023-01-16)

Features

  • 🎸 add text content option (be9c114)
  • 🎸 allow nil content (bff69ef)
  • 🎸 expose width input (0412cf9)

6.4.0 (2023-01-10)

Features

  • 🎸 expose width input (0412cf9)

6.3.0 (2023-01-05)

Features

  • 🎸 expose animation input (8cfcb35)

6.2.2 (2023-01-05)

Bug Fixes

6.2.1 (2023-01-05)

Bug Fixes

  • 🐛 move data open to a better hool (6e68832)

6.2.0 (2022-12-28)

Features

  • 🎸 add data-tippy-open attribute on the host (0ea15c4)

6.1.0 (2022-12-26)

Features

  • 🎸 add variation class (c82f4b8)

6.0.0 (2022-11-27)

⚠ BREAKING CHANGES

  • upgrade to Angular 15

  • Remove TippyModule

  • Peer dependency is now ng v15

Features

5.1.4 (2022-02-04)

Bug Fixes

  • 🐛 isVisible should run inside the zone (96128c2)

5.1.3 (2022-02-04)

Bug Fixes

  • 🐛 isVisible should run inside the zone (90901de)

5.1.2 (2022-01-31)

Bug Fixes

  • 🐛 support onpush components (6bb9097)

5.1.1 (2021-12-29)

Bug Fixes

  • 🐛 filter out custom props (d15f38b)

5.1.0 (2021-12-23)

Features

  • 🎸 preserve view when creating tooltip via service (d497bd4)

5.0.2 (2021-11-25)

Bug Fixes

  • 🐛 dont create tippy on server side (2dbf2c4), closes #77

5.0.1 (2021-11-25)

Bug Fixes

  • update only modified properties on change (0345b52)

5.0.0 (2021-11-24)

⚠ BREAKING CHANGES

  • Peer dep of angular 13

Features

4.3.0 (2021-11-23)

Features

  • pass data to custom component (53224d1), closes #74

Bug Fixes

  • add context to ownProps to remove from tippy config (c096ef2)

4.2.0 (2021-08-09)

Features

  • 🎸 allow passing context to component (e342668)

4.1.1 (2021-07-23)

Bug Fixes

  • 🐛 check duplicate component (2263bed), closes #65

4.1.0 (2021-07-18)

Features

  • declarativly toggle visibility (e9f0e89)

4.0.2 (2021-07-16)

Bug Fixes

4.0.1 (2021-07-15)

Bug Fixes

  • 🐛 revert custom host (a5e72d6)

4.0.0 (2021-07-15)

⚠ BREAKING CHANGES

  • When providing a string as content, we use innertext instead to prevent xss

Features

  • 🎸 don't use innerhtml when passing a string (cb4f2f7)

3.7.1 (2021-07-11)

Bug Fixes

  • correct instance to run showOnCreate (test) (a3e5dc8)
  • correct instance to run showOnCreate correct (976ec37)

3.6.0 (2021-06-30)

Features

3.5.3 (2021-06-07)

3.5.2 (2021-06-07)

Bug Fixes

  • 🐛 filter invalid class names (987082b)

3.5.1 (2021-06-07)

Bug Fixes

  • 🐛 support multipule class names in service (9a7fd8e)

3.5.0 (2021-06-07)

Features

  • 🎸 support multiple class names (3f6724c)
  • add hideOnEscapeButton to close popper (dab90fc)

3.4.0 (2021-06-06)

Features

  • added config 'disableOnNilValue' to disable tippy if value is nil (3a27fa0)
  • added config 'disableOnNilValue' to skip instance if value is nil (8c14a5e)
  • only create tippy instance in case of non-nil value (f712880)
  • only create tippy instance in case of non-nil value (06b8d7d)
  • removed config 'disableOnNilValue' in favour of default behaviour (8bfd4b6)

3.3.0 (2021-06-06)

Features

  • added config 'disableOnNilValue' to disable tippy if value is nil (3a27fa0)
  • added config 'disableOnNilValue' to skip instance if value is nil (8c14a5e)
  • only create tippy instance in case of non-nil value (f712880)
  • only create tippy instance in case of non-nil value (06b8d7d)
  • removed config 'disableOnNilValue' in favour of default behaviour (8bfd4b6)

3.2.1 (2021-05-24)

Bug Fixes

3.2.0 (2021-05-24)

Features

  • 🎸 support class name property when using the service (0a8218b)

3.1.1 (2021-03-14)

Bug Fixes

  • 🐛 add maxwidth to child element (fbbd4cc)

3.1.0 (2021-02-08)

Features

  • add ngneat/overview to package.json by schematics (54d0998)

3.0.0 (2021-01-28)

⚠ BREAKING CHANGES

  • ngneat/overview is external dep

Features

  • 🎸 remove ngneat overview from deps (ea8212e)

2.1.1 (2021-01-05)

Bug Fixes

  • 🐛 listen to host resize when using host width (dfb9b45)

2.1.0 (2020-12-30)

Bug Fixes

  • 🐛 fix variation and warnings (c6eec8b)

Tests

1.6.0 (2020-12-22)

Features

  • 🎸 add use host width property (922ce01)

2.0.0 (2020-12-29)

⚠ BREAKING CHANGES

  • the api is simpler now checkout the readme

Features

Tests

1.5.5 (2020-11-28)

Bug Fixes

  • specify module type in ModuleWithProviders (cef0703)

1.5.4 (2020-11-22)

Bug Fixes

  • 🐛 several intersection events on the same tick (6eab305)

1.5.3 (2020-11-20)

Bug Fixes

  • 🐛 reset tooltip on content change (5a53e16)

1.5.2 (2020-11-18)

Bug Fixes

1.5.1 (2020-11-16)

Bug Fixes

  • allow <ng-container> and multiple child nodes (e648877)
  • allow <ng-container> and multiple child nodes (e2b666b)
  • support server-side rendering (2602c3d)

Tests

  • fix Cypress tests related to scrolling (743f1f8)

1.5.0 (2020-11-12)

Features

  • 🎸 add maxwidth input (1361d2b)

1.4.1 (2020-11-11)

Bug Fixes

  • 🐛 use animation frame (d1cb240)

1.4.0 (2020-09-22)

Features

  • doc: add new table to manage Outputs (25d0778)
  • lib: update Subject name into "helipopperVisible" (3744d91)
  • add @output() method to expose current tooltip status as boolean (409ad7f)

1.3.2 (2020-08-30)

Bug Fixes

1.3.1 (2020-08-30)

Bug Fixes

1.3.0 (2020-08-30)

Features

  • 🎸 add allow close input (236a9dc)

1.2.0 (2020-08-30)

Features

Bug Fixes

  • schematics: library name (35c4f32)

1.1.0 (2020-06-15)

Features

1.0.0 (2020-06-12)