---
title: Headless Tippy
path: /v6/headless-tippy/
index: 14
---

"Headless Tippy" refers to Tippy without any of the default element rendering or
CSS. This allows you to create your own element from scratch and use Tippy for
its logic only.

This means all props marked with the R symbol in [All Props](../all-props/) no
longer "work" by default, since it's your responsibility to create these
features.

### Imports

Replace your imports:

```diff
- import tippy from 'tippy.js';
+ import tippy from 'tippy.js/headless';
```

```diff
- <script src="https://unpkg.com/tippy.js@6"></script>
+ <script src="https://unpkg.com/tippy.js@6/headless/dist/tippy-headless.umd.min.js"></script>
```

When using Headless Tippy, all imports (e.g. `followCursor`) should also be
specified from this import, otherwise you will end up with duplicate code bloat.

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

#### Use in conjunction with Default Tippy

You can use Tippy in headless mode when using the default import as well, but
you should disable animations by default:

```js
import tippy from 'tippy.js';

// This ensures your tippy will unmount if you haven't yet implemented
// animations.
tippy.setDefaultProps({animation: false});
```

### Usage

The `render` prop is how you create your own tippy element:

```js
import tippy from 'tippy.js/headless';

tippy(targets, {
  content: 'Hello!',
  render(instance) {
    // The recommended structure is to use the popper as an outer wrapper
    // element, with an inner `box` element
    const popper = document.createElement('div');
    const box = document.createElement('div');

    popper.appendChild(box);

    box.className = 'my-custom-class';
    box.textContent = instance.props.content;

    function onUpdate(prevProps, nextProps) {
      // DOM diffing
      if (prevProps.content !== nextProps.content) {
        box.textContent = nextProps.content;
      }
    }

    // Return an object with two properties:
    // - `popper` (the root popper element)
    // - `onUpdate` callback whenever .setProps() or .setContent() is called
    return {
      popper,
      onUpdate, // optional
    };
  },
});
```

### Animations

When using Headless Tippy, animations are not enabled (`animation: false`) by
default.

To enable animations, set `animation: true` (or a `string`) — this will require
you to invoke `instance.unmount()` whenever your hide animation completes.

```js
tippy(targets, {
  animation: true,
  onHide(instance) {
    // perform your hide animation in here, then once it completes, call
    // instance.unmount()

    // Example: unmounting must be async (like a real animation)
    requestAnimationFrame(instance.unmount);
  },
});
```

### Mutations

Tippy performs a few mutations to your popper element, so you should avoid
specifying these:

#### Attributes

- `id` attribute is set (for accessibility)
- `data-tippy-root` attribute is set (to allow for nesting)

#### Properties

- `_tippy` property is set on it (to allow for nesting)

#### Styles

- `pointerEvents` is changed based on the `interactive` prop
- `transition` is changed based on the `moveTransition` prop
- `zIndex` is changed based on the `zIndex` prop
