The future of Popper is here! Floating UI is now available. Get it now!
Popper Logo
Popper Logo

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! Follow the tutorial below to learn how to use Popper.

Don't mind tech-related ads? Consider disabling your ad-blocker to help us!
They are small and unobtrusive.
Alternatively, support us on Open Collective!

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"></script>
    <script>
      const button = document.querySelector('#button');
      const tooltip = document.querySelector('#tooltip');

      const popperInstance = 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;
  background: inherit;
}

#arrow {
  visibility: hidden;
}

#arrow::before {
  visibility: visible;
  content: '';
  transform: rotate(45deg);
}

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:

const popperInstance = 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', '');

  // We need to tell Popper to update the tooltip position
  // after we show the tooltip, otherwise it will be incorrect
  popperInstance.update();
}

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 can disable the event listeners when the tooltip is hidden to optimize it:

function show() {
  // Make the tooltip visible
  tooltip.setAttribute('data-show', '');

  // Enable the event listeners
  popperInstance.setOptions((options) => ({
    ...options,
    modifiers: [
      ...options.modifiers,
      { name: 'eventListeners', enabled: true },
    ],
  }));

  // Update its position
  popperInstance.update();
}

function hide() {
  // Hide the tooltip
  tooltip.removeAttribute('data-show');

  // Disable the event listeners
  popperInstance.setOptions((options) => ({
    ...options,
    modifiers: [
      ...options.modifiers,
      { name: 'eventListeners', enabled: false },
    ],
  }));
}

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;
        background: inherit;
      }

      #arrow {
        visibility: hidden;
      }

      #arrow::before {
        visibility: visible;
        content: '';
        transform: rotate(45deg);
      }

      #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"></script>
    <script>
      const button = document.querySelector('#button');
      const tooltip = document.querySelector('#tooltip');

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

      function show() {
        // Make the tooltip visible
        tooltip.setAttribute('data-show', '');

        // Enable the event listeners
        popperInstance.setOptions((options) => ({
          ...options,
          modifiers: [
            ...options.modifiers,
            { name: 'eventListeners', enabled: true },
          ],
        }));

        // Update its position
        popperInstance.update();
      }

      function hide() {
        // Hide the tooltip
        tooltip.removeAttribute('data-show');

        // Disable the event listeners
        popperInstance.setOptions((options) => ({
          ...options,
          modifiers: [
            ...options.modifiers,
            { name: 'eventListeners', enabled: false },
          ],
        }));
      }

      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.

Edit this page
v2.×
© 2024 MIT License