---
title: Plugins
path: /v6/plugins/
index: 13
---

Plugins are an extensible way to add functionality to tippy instances. By
splitting functionality into a plugin, components or routes that don't need the
plugin are not burdened with its bundle size cost.

### Exported plugins

These plugins are exported by the package:

- `animateFill`
- `followCursor`
- `inlinePositioning`
- `sticky`

### Usage

#### CDN (umd)

Included plugins (part of the [All Props](/all-props/) table) will work as
normal.

```js
tippy(targets, {
  followCursor: true,
});
```

#### Modules (esm)

Modules have the benefit of tree-shaking, so explicitly importing the plugin is
required:

```js
import tippy, {followCursor} from 'tippy.js';

tippy(targets, {
  followCursor: true,
  plugins: [followCursor],
});
```

### Creating your own custom plugin

A plugin is created by defining an object with the following shape:

```js
const plugin = {
  // Optional (if the plugin provides a prop to use)
  name: 'propName', // e.g. 'followCursor' or 'sticky'
  defaultValue: 'anyValue',

  // Required
  fn(instance) {
    // Internal state
    return {
      // Lifecycle hooks
    };
  },
};
```

The plugin's function `fn` returns an object of lifecycle hooks. Plugins are
invoked per-instance and the plugin function definition takes the instance as an
argument, so you can use private variables to create internal state in the
plugin closure.

Here are some examples:

#### hideOnPopperBlur

Causes a popper to hide if no elements within it are in focus.

```js
const hideOnPopperBlur = {
  name: 'hideOnPopperBlur',
  defaultValue: true,
  fn(instance) {
    return {
      onCreate() {
        instance.popper.addEventListener('focusout', (event) => {
          if (
            instance.props.hideOnPopperBlur &&
            event.relatedTarget &&
            !instance.popper.contains(event.relatedTarget)
          ) {
            instance.hide();
          }
        });
      },
    };
  },
};
```

#### globalStore

Manages tippy instances within a global store.

```js
let tippyInstances = [];

const globalStore = {
  fn(instance) {
    return {
      onCreate() {
        tippyInstances.push(instance);
      },
      onDestroy() {
        tippyInstances = tippyInstances.filter((i) => i !== instance);
      },
    };
  },
};

// APIs to manipulate all tippy instances
export function updateAll(props) {
  tippyInstances.forEach((instance) => {
    instance.setProps(props);
  });
}
```

#### hideOnEsc

Hides the tippy when the escape key is pressed.

```js
const hideOnEsc = {
  name: 'hideOnEsc',
  defaultValue: true,
  fn({hide}) {
    function onKeyDown(event) {
      if (event.keyCode === 27) {
        hide();
      }
    }

    return {
      onShow() {
        document.addEventListener('keydown', onKeyDown);
      },
      onHide() {
        document.removeEventListener('keydown', onKeyDown);
      },
    };
  },
};
```

<Demo>
  <Tippy hideOnEsc>
    <Button>Focus me then press `esc`</Button>
  </Tippy>
</Demo>

### TypeScript

Types that take `Props` (e.g. `Tippy`, `Delegate`, `CreateSingleton`) are
generics that accept an extended props interface:

```ts
import tippy, {Tippy, Props, Plugin, LifecycleHooks} from 'tippy.js';

interface CustomProps {
  myCustomProp: boolean;
}

type FilteredProps = CustomProps &
  Omit<Props, keyof CustomProps | keyof LifecycleHooks>;

type ExtendedProps = FilteredProps & LifecycleHooks<FilteredProps>;

export const myCustomProp: Plugin<ExtendedProps> = {
  name: 'myCustomProp',
  defaultValue: false,
  fn: () => ({}),
};

export default (tippy as unknown) as Tippy<ExtendedProps>;
```
