Popper Logo
Popper Logo
  • Home

Tutorial

In this tutorial you'll learn how to use Popper by building a basic tooltip.

Remember, Popper is not a tooltip library, it's the foundation to build one! If what you are looking for is a ready to use tooltip lib, take a look at Tippy.js.

Setting up

Create a new HTML document with two elements, a <button> and a tooltip <div>, and pass them to Popper:

<!DOCTYPE html>
<html>
  <head>
    <title>Popper Tutorial</title>
  </head>
  <body>
    <button id="button" aria-describedby="tooltip">My button</button>
    <div id="tooltip" role="tooltip">My tooltip</div>

    <script src="https://unpkg.com/@popperjs/core@2.0.0"></script>
    <script>
      const button = document.querySelector('#button');
      const tooltip = document.querySelector('#tooltip');

      Popper.createPopper(button, tooltip);
    </script>
  </body>
</html>

Styling

Let's give our tooltip some styling:

<!DOCTYPE html>
<html>
  <head>
    <title>Popper Tutorial</title>
    <style>
      #tooltip {
        background: #333;
        color: white;
        font-weight: bold;
        padding: 4px 8px;
        font-size: 13px;
        border-radius: 4px;
      }
    </style>
  </head>
  <body>
    <!-- ... -->
  </body>
</html>

Here's the result so far:

Arrow

Our tooltip is currently just a box. In many UI design systems, this is all tooltips need, but others prefer to have an arrow that points toward the reference element.

Add an arrow element with a data-popper-arrow attribute like so:

<div id="tooltip" role="tooltip">
  My tooltip
  <div id="arrow" data-popper-arrow></div>
</div>

Now for styling:

#arrow,
#arrow::before {
  position: absolute;
  width: 8px;
  height: 8px;
  z-index: -1;
}

#arrow::before {
  content: '';
  transform: rotate(45deg);
  background: #333;
}

The ::before pseudo-element is required because Popper uses transform to position the arrow inside the popper, but we want to use our own transform to rotate the arrow box into a diamond.

Now we need to offset the arrow depending on the popper's current placement. Popper provides this information with the data-popper-placement attribute:

#tooltip[data-popper-placement^='top'] > #arrow {
  bottom: -4px;
}

#tooltip[data-popper-placement^='bottom'] > #arrow {
  top: -4px;
}

#tooltip[data-popper-placement^='left'] > #arrow {
  right: -4px;
}

#tooltip[data-popper-placement^='right'] > #arrow {
  left: -4px;
}

Why the ^=? This means "starts with", because we can also have variation placements like top-start.

Here's the result so far:

Offset

Our arrow currently overlaps the reference, we can give it a distance of 8px using the offset modifier:

Popper.createPopper(button, tooltip, {
  modifiers: [
    {
      name: 'offset',
      options: {
        offset: [0, 8],
      },
    },
  ],
});

Here's the result so far:

Functionality

We only want our tooltip to show when hovering or focusing the button.

#tooltip {
  /* ... */
  display: none;
}

#tooltip[data-show] {
  display: block;
}
function show() {
  tooltip.setAttribute('data-show', '');
}

function hide() {
  tooltip.removeAttribute('data-show');
}

const showEvents = ['mouseenter', 'focus'];
const hideEvents = ['mouseleave', 'blur'];

showEvents.forEach(event => {
  button.addEventListener(event, show);
});

hideEvents.forEach(event => {
  button.addEventListener(event, hide);
});

Here's the final result:

Performance

Once we create a popper with createPopper, it stays "alive". This means while scrolling the page, the popper is constantly being updated, even if it is not visible. We need to create and destroy the popper instance when necessary:

let popperInstance = null;

function create() {
  popperInstance = Popper.createPopper(button, tooltip, {
    modifiers: [
      {
        name: 'offset',
        options: {
          offset: [0, 8],
        },
      },
    ],
  });
}

function destroy() {
  if (popperInstance) {
    popperInstance.destroy();
    popperInstance = null;
  }
}

function show() {
  tooltip.setAttribute('data-show', '');
  create();
}

function hide() {
  tooltip.removeAttribute('data-show');
  destroy();
}

Full code

<!DOCTYPE html>
<html>
  <head>
    <title>Popper Tutorial</title>
    <style>
      #tooltip {
        background: #333;
        color: white;
        font-weight: bold;
        padding: 4px 8px;
        font-size: 13px;
        border-radius: 4px;
        display: none;
      }

      #tooltip[data-show] {
        display: block;
      }

      #arrow,
      #arrow::before {
        position: absolute;
        width: 8px;
        height: 8px;
        z-index: -1;
      }

      #arrow::before {
        content: '';
        transform: rotate(45deg);
        background: #333;
      }

      #tooltip[data-popper-placement^='top'] > #arrow {
        bottom: -4px;
      }

      #tooltip[data-popper-placement^='bottom'] > #arrow {
        top: -4px;
      }

      #tooltip[data-popper-placement^='left'] > #arrow {
        right: -4px;
      }

      #tooltip[data-popper-placement^='right'] > #arrow {
        left: -4px;
      }
    </style>
  </head>
  <body>
    <button id="button" aria-describedby="tooltip">My button</button>
    <div id="tooltip" role="tooltip">
      My tooltip
      <div id="arrow" data-popper-arrow></div>
    </div>

    <script src="https://unpkg.com/@popperjs/core@2.0.0"></script>
    <script>
      const button = document.querySelector('#button');
      const tooltip = document.querySelector('#tooltip');

      let popperInstance = null;

      function create() {
        popperInstance = Popper.createPopper(button, tooltip, {
          modifiers: [
            {
              name: 'offset',
              options: {
                offset: [0, 8],
              },
            },
          ],
        });
      }

      function destroy() {
        if (popperInstance) {
          popperInstance.destroy();
          popperInstance = null;
        }
      }

      function show() {
        tooltip.setAttribute('data-show', '');
        create();
      }

      function hide() {
        tooltip.removeAttribute('data-show');
        destroy();
      }

      const showEvents = ['mouseenter', 'focus'];
      const hideEvents = ['mouseleave', 'blur'];

      showEvents.forEach(event => {
        button.addEventListener(event, show);
      });

      hideEvents.forEach(event => {
        button.addEventListener(event, hide);
      });
    </script>
  </body>
</html>

Finished

You've now created a basic tooltip using Popper! Of course, there is more you can do, such as adding animations or interactivity. These are up to you to explore when creating abstractions for common popper elements like tooltips, popovers, drop-downs, and more.

Home