5 best popover libraries for React
Tags: react, webdev
Published: 2023-11-01 16:25:30

**Written by [Ibadehin Mojeed](https://blog.logrocket.com/author/ibadehinmojeed/)✏️**

Popovers are essential and commonly used UI elements that reveal additional information when a user interacts with a designated element. Examples of professional use cases for popovers include [dropdown navigation menus](https://blog.logrocket.com/how-create-multilevel-dropdown-menu-react/), [date pickers](https://blog.logrocket.com/top-react-date-pickers/), and [guided tours during onboarding processes](https://blog.logrocket.com/using-driver-js-guide-user-focus/). 

Within the React ecosystem, many libraries cater to this need, offering varying levels of customization, position strategies, and responsiveness. In this comprehensive guide, we’ll explore five standout popover and positioning libraries for React:

1.  [Popper.js](#popper-js)
2.  [`react-tiny-popover`](#react-tiny-popover)
3.  [react-laag](#react-laag)
4.  [`reactjs-popup`](#reactjs-popup)
5.  [React Joyride](#react-joyride)

For each of these libraries, we will delve into their setup, positioning capabilities, responsiveness, and more, using the same basic demo project to see each library in action. You can check out the [code and demos on CodeSandbox](https://codesandbox.io/s/funny-burnell-c7tfcj?file=/src/components/no-library/TableRow.js) or jump to the end to see a [comparison table for all five libraries](#comparison-table-react-popover-libraries). 

Before we dive into the libraries, let’s first discuss why you should consider using popover libraries rather than building your popovers from scratch.

## Why popover libraries?

Creating a robust popover widget from scratch can be complex for many reasons. For example, it’s crucial to ensure that you’re handling numerous edge cases thoroughly. These complexities can pose significant challenges to your development process. 

Let's take a closer look at a simple, reusable popover component created from scratch. 

Imagine a dashboard page where users can choose between **Admin** and **User** roles for themselves. When users click on the button to set their role, a popover will appear, presenting the available roles. 

Achieving this in React involves creating a component state to toggle the popover content and applying a `position: absolute` on the popover to align it with the reference element. The following code should render a customized popover:

```javascript
const TableRow = ({ ... }) => {
  const [isPopoverOpen, setIsPopoverOpen] = useState(false);

  return (
    // ...
    <Popover
      isOpen={isPopoverOpen}
      content={<div className="popover-content">{popup}</div>}
    >
      <div
        className="btn-container"
        onClick={() => setIsPopoverOpen(!isPopoverOpen)}
      >
        <Button label="Admin" />
        <RxCaretSort />
      </div>
    </Popover>
    // ...
  );
};
```

The actual `Popover` component will look like so:

```javascript
const Popover = ({ isOpen, content, children }) => {
  return (
    <div className="no-library">
      {children}
      {isOpen && content}
    </div>
  ); 
};

export default Popover;
```

The GIF below demonstrates the outcome: ![Demo Of Popover Built From Scratch In React Showing Two Dummy Users With Option To Set Role As Admin Or User. Mouse Shown Clicking On Each Button To Open Popover To Set Role](https://blog.logrocket.com/wp-content/uploads/2023/10/img1-Popover-demo-built-scratch-React.gif) See the [code on CodeSandbox](https://codesandbox.io/s/funny-burnell-c7tfcj?file=/src/components/no-library/TableRow.js). While this demo works fine at the moment, there are two significant problems that can occur — placement and clipping — each with many potential edge cases to address. Let's explore these now.

### The placement problem

Currently, the reference element — represented by the button in the GIF above — is centrally positioned, so the popover works properly. However, when the element is near the right edge of the browser, it may lead to overflow and necessitate scrolling. 

To prevent this overflow, you can make some manual adjustments. While manual adjustment is fine when implementing the popover in a single location, for a reusable component, we must incorporate dynamic positioning logic. 

This placement problem becomes particularly important when we need to account for diverse screen sizes and layouts. Unfortunately, addressing all of those potential display scenarios can delay the development process significantly.

### Clipping issues

It's unpredictable where a reusable popover component might end up. It could be situated within a container with styles that could disrupt the popup, like `overflow: hidden` or other styles. 

In such a scenario, if the element is near the bottom edge of the boundary, the popover will be truncated: ![Example Of The Clipping Issue Presented By Popovers Built From Scratch In React. Popover Shown Getting Cut Off For Bottom User](https://blog.logrocket.com/wp-content/uploads/2023/10/img2-Example-clipping-issue-popover-built-scratch.gif) We can enable the popover to appear outside and disregard the `overflow: hidden` of the parent element by [utilizing React Portals](https://blog.logrocket.com/learn-react-portals-example/):

```javascript
import { createPortal } from "react-dom";

const Popover = ({ isOpen, content, children }) => {
  return (
    <div>
      {children}
      {isOpen && createPortal(content, document.body)}
    </div>
  );
};

export default Popover;
```

The `createPortal` function allows us to render the content directly into a specified DOM node — in this case, within the `<body>`. 

Using a portal ensures that the popover content escapes the constraints of table overflow styling. However, it comes with the drawback that the content loses its original positioning: ![Using React Portals On Popovers Built From Scratch To Address Clipping Issue From Previous Image. Popover Appears In Bottom Left Corner Of Image Rather Than Directly Below Dropdown](https://blog.logrocket.com/wp-content/uploads/2023/10/img3-Using-Portals-popovers-built-scratch-React-solve-clipping-issue.gif) Instead of investing development hours in addressing edge cases, let's explore standout libraries that help tackle these issues in an elegant and performant manner.

## 1\. Popper.js: Popover and tooltip positioning engine <a name="popper-js">

[Popper.js is a positioning library](https://blog.logrocket.com/using-react-popper-styled-components/) designed for elements that "pop," including popovers. 

Technically, Popper.js is not a popover library in itself — we still have to create the popover as demonstrated earlier. However, it significantly simplifies the process of constructing a robust popover by handling complexities related to placement, overflow, and flipping. 

Due to its capabilities, various UI libraries such as Material UI, Foundation, and Bootstrap incorporate Popper.js to ensure precise positioning of popup elements. Similarly, React developers frequently rely on it as a foundational tool for creating custom popovers. 

While not exclusive to React, Popper.js provides a `react-popper` wrapper, making it easier to incorporate Popper's powerful positioning capabilities into React applications. 

Now, let's explore how the Popper library can assist in directly attaching the popover to the reference button.

### Setting up Popper and using it with Portals

First, let’s install `@popperjs/core` and its React wrapper:

```bash
npm i react-popper @popperjs/core
```

In the earlier example, we observed how the `createPortal` function renders the popover content in a different part of the DOM. Now, let's employ the Popper library to precisely address the positioning. 

The `react-popper` wrapper provides a `usePopper` Hook that requires three arguments: a `referenceElement`, `popperElement`, and a configuration object. In this configuration, we can specify the initial placement of the popover:

```javascript
import { useState } from "react";

import { usePopper } from "react-popper";
// ...
const TableRow = ({ name, email, popup }) => {
  // ...
  const [referenceElement, setReferenceElement] = useState(null);
  const [popperElement, setPopperElement] = useState(null);

  const { styles, attributes } = usePopper(referenceElement, popperElement, {
    placement: "bottom-start",
    modifiers: [{ name: "offset", options: { offset: [0, 5] } }]
  });

  return (
    // ...
    <Popover
      isOpen={isPopoverOpen}
      content={
        <div
          ref={setPopperElement}
          style={styles.popper}
          {...attributes.popper}
          className="popover-content"
        >
          {popup}
        </div>
      }
    >
      <div
        className="btn-container"
        ref={setReferenceElement}
        onClick={() => setIsPopoverOpen(!isPopoverOpen)}
      >
        <Button label="Admin" />
        <RxCaretSort />
      </div>
    </Popover>
    // ...
  );
};

export default TableRow;
```

The `usePopper` Hook returns an object containing styles and attributes necessary for positioning the popover along with the reference element. To enable proper positioning, we assigned the popper and reference setters to the `ref` attributes for both the popover content and the reference element. 

With this implementation, the popover should now seamlessly attach to the button, escaping any overflow hidden in the parent container and achieving the correct positioning. The result should look like so: ![Popover Demo Created Using Popper Js Library For Similar Result To First Image](https://blog.logrocket.com/wp-content/uploads/2023/10/img4-Using-Popper-js-library-create-popover-React.gif) See the [project demo](https://c7tfcj.csb.app/react-popper-createportal) and its [source code on CodeSandbox](https://codesandbox.io/s/funny-burnell-c7tfcj?file=/src/components/react-popper-createportal/TableRow.js).

### Using `react-popper` without `createPortal`

Let's explore how the Popper library manages positioning without utilizing the `createPortal` function. After removing it from the code, we now have the following:

```javascript
// import { createPortal } from "react-dom";

const Popover = ({ isOpen, content, children }) => {
  return (
    <div>
      {children}
      {isOpen && content}
      {/* {isOpen && createPortal(content, document.body)} */}
    </div>
  );
};

export default Popover;
```

The library is intelligent enough to flip the popover content to a different placement that fits better, thanks to its ability to handle `offsetParent` contexts. There's no need to move the popover outside of its original DOM context: ![Demo Of Using Popper Js Library Without React Portals](https://blog.logrocket.com/wp-content/uploads/2023/10/img5-Using-Popper-js-without-Portals.gif) See the [source code on CodeSandbox](https://codesandbox.io/s/funny-burnell-c7tfcj?file=/src/components/react-popper/TableRow.js) and [check out the live demo](https://c7tfcj.csb.app/react-popper) to see for yourself. 

Popper.js is an excellent choice if you want to create your own custom popover, as it‘s capable of managing complex, dynamic positioning logic effortlessly. However, if you're seeking a ready-made popover component with comprehensive features, let's explore the next library.

## 2\. `react-tiny-popover`: A lightweight but brilliant library <a name="react-tiny-popover">

If you're in search of simplicity and a minimal footprint, `react-tiny-popover` stands out as an excellent choice for constructing popovers. 

Despite its lightweight nature, this library is robust and provides great customization options. Moreover, it features a responsive design by default, ensuring compatibility across various screen sizes.

### Setting up and using `react-tiny-popover`

You can install `react-tiny-popover` as-is with no other dependencies:

```bash
npm install react-tiny-popover –save
```

Using this library is straightforward. Instead of creating a custom popover component, this library provides us with one that we can directly render:

```javascript
// ...
import { Popover, ArrowContainer } from "react-tiny-popover";

const TableRow = ({ name, email, popup }) => {
  const [isPopoverOpen, setIsPopoverOpen] = useState(false);
  return (
    // ...
    <Popover
      isOpen={isPopoverOpen}
      positions={["top", "bottom", "left", "right"]}
      align="start"
      padding={8}
      onClickOutside={() => setIsPopoverOpen(false)}
      content={({ position, childRect, popoverRect }) => (
        <ArrowContainer
          position={position}
          childRect={childRect}
          popoverRect={popoverRect}
          arrowColor={"#2a2e2d"}
          arrowSize={8}
        >
          <div>
            <div className="popover-content">{popup}</div>
          </div>
        </ArrowContainer>
      )}
    >
      <div
        onClick={() => setIsPopoverOpen(!isPopoverOpen)}
        className="btn-container"
      >
        <Button label="Admin" />
        <RxCaretSort />
      </div>
    </Popover>
    // ...
  );
};
```

The `Popover` component from the library requires three props:

*   `isOpen` to track visibility
*   `children`, which represents the reference element in this case
*   `content`, which takes the content that will appear as the popover

The expected behavior of the `Popover` component is as follows: ![Popover Demo Created Using React Tiny Popover Library](https://blog.logrocket.com/wp-content/uploads/2023/10/img6-Example-popover-created-react-tiny-popover.gif) Similar to the positioning capability offered by Popper.js, `react-tiny-popover` can also guard against container boundaries and reposition itself to prevent hidden overflow. However, unlike Popper, this library by default moves the popover content outside of its DOM context, appending it to the `document.body`: ![Browser Dev Tools Open To Show Popover Content Outside Dom Context And Appended To Document Body, Which React Tiny Popover Library Does By Default](https://blog.logrocket.com/wp-content/uploads/2023/10/img7-Popover-content-shown-outside-DOM-context-appended-documentbody-default-react-tiny-popover.png) To position the popover adjacent to the button that activates it, include a `parentElement` prop, and then wrap the `Popover` with a reference element:

```javascript
import { useState, useRef } from "react";
// ...
const TableRow = ({ name, email, popup }) => {
  // ...
  const boxContainerRef = useRef();
  return (
    // ...
      <div className="popover-container" ref={boxContainerRef}>
        <Popover
          // ...
          parentElement={boxContainerRef.current}
        >
          {...}
        </Popover>
    </div>
    //   ...
  );
};
```

The popover should now be attached to the `container` element instead of the `body` element: ![Browser Dev Tools Open To Show Popover Now Attached To Container Element Instead Of Body Element](https://blog.logrocket.com/wp-content/uploads/2023/10/img8-Popover-attached-container-element-instead-body-element.png) If you prefer a popover without an arrow, you can modify the `content` prop to exclude the `ArrowContainer` like so:

```javascript
content={<div className="popover-content">{popup}</div>}
```

See [the source code and demo on CodeSandbox](https://codesandbox.io/s/react-popover-libraries-c7tfcj?file=/src/components/react-tiny-popover/TableRow.js).

## 3\. react-laag <a name="react-laag">

react-laag provides a Hook for positioning popovers. Similar to Popper.js, there's still some effort involved in creating the visual aspects of the popover. The library takes care of intricate calculations to achieve precise positioning. 

Its popularity has been growing recently, going from less than 50 thousand weekly npm downloads to over 138 thousand over the past year: ![Growing Popularity Of React Laag Library Shown By Number Of Npm Downloads](https://blog.logrocket.com/wp-content/uploads/2023/10/img9-Popularity-react-laag-library-npm-downloads.png)

### Setting up and using react-laag

Like `react-tiny-popover`, you can install react-laag with zero dependencies:

```bash
npm install react-laag
```

Here is a quick example of integrating the library into our project:

```javascript
// ...
import { useLayer, Arrow } from "react-laag";

const TableRow = ({ name, email, popup }) => {
  const [isOpen, setIsOpen] = useState(false);

  const { renderLayer, triggerProps, layerProps, arrowProps } = useLayer({
    isOpen,
    onOutsideClick: () => setIsOpen(false),
    onDisappear: () => setIsOpen(false),
    overflowContainer: false,
    auto: true,
    placement: "right-start",
    triggerOffset: 12,
    arrowOffset: 16
  });

  return (
    // ...
    <Popover
      isOpen={isOpen}
      content={renderLayer(
        <div className="popover-content" {...layerProps}>
          {popup}
          <Arrow {...arrowProps} backgroundColor="#2a2e2d" />
        </div>
      )}
    >
      <div
        {...triggerProps}
        onClick={() => setIsOpen(!isOpen)}
        className="btn-container"
      >
        <Button label="Admin" />
        <RxCaretSort />
      </div>
    </Popover>
    // ...
  );
};
```

Despite its primary focus on positioning, this library provides essential popover features — such as the `onOutsideClick` function — to manage the outside click event that closes the popover. Refer to the expected behavior in the demo below: ![Demo Of Popover Created With React Laag Library](https://blog.logrocket.com/wp-content/uploads/2023/10/img10-Demo-popover-created-react-laag-library.gif) See this react-laag demo’s [source code on CodeSandbox](https://codesandbox.io/s/react-popover-libraries-c7tfcj?file=/src/components/react-laag/TableRow.js).

## 4\. `reactjs-popup` <a name="reactjs-popup">

The `reactjs-popup` popover library is an excellent choice for those seeking versatility in a simple package. It offers a fully accessible popup component, improving the user experience for individuals who rely on keyboard navigation.

### Setting up and using `reactjs-popup`

Installing `reactjs-popup` is straightforward. Simply use this command:

```bash
npm i reactjs-popup
```

The code below demonstrates how you can create a popover using `reactjs-popup`:

```javascript
import Popup from "reactjs-popup";

const TableRow = ({ name, email, popup }) => {
  return (
    // ...
    <Popup
      trigger={
        <div className="btn-container">
          <Button label="Admin" />
          <RxCaretSort />
        </div>
      }
      position={["top left", "top right", "bottom left", "bottom right"]}
      closeOnDocumentClick
      arrowStyle={{ color: "#2a2e2d" }}
    >
      <div className="popover-content">{popup}</div>
    </Popup>
    // ...
  );
};
```

This represents the simplest popover implementation for our hypothetical project that we’ve seen so far. `reactjs-popup` provides us with an easy-to-use `Popup` component and handles complex positioning very well. Feel free to explore [the source code](https://codesandbox.io/s/react-popover-libraries-c7tfcj?file=/src/components/reactjs-popup/TableRow.js) and [demo on CodeSandbox](https://c7tfcj.csb.app/reactjs-popup).

## 5\. React Joyride: Guided tours with popovers <a name="react-joyride">

React Joyride takes a unique approach to popovers, specializing in popovers designed for guided tours within React applications. It's a standout choice for onboarding new users in a dynamic and engaging way. 

Before we implement this library, let’s see a quick demo of what it entails: ![Demo Of Guided Tour With Popovers Created Using React Joyride Library To Walk User Through Using Demo Project](https://blog.logrocket.com/wp-content/uploads/2023/10/img11-Demo-React-Joyride-guided-tour-popovers.gif) 

As we can see, React Joyride provides sufficient control over popover positioning to ensure a smooth user experience. It can adapt to different screen sizes, making it ideal for onboarding experiences.

### Setting up and using React Joyride

Installing React Joyride is simple, requiring only the following command with no other dependencies:

```bash
npm i react-joyride
```

To use this library, you need to define the steps for the guided tour. Each step will include a `target` property that points to the appropriate element:

```javascript
import { useState } from "react";
import Joyride from "react-joyride";
// ...

const TableRow = ({ name, email, popup }) => {

  const [{ run, steps }] = useState({
    run: true,
    steps: [
      {
        content: <h2>Tour this app with me!</h2>,
        locale: { skip: <strong>SKIP</strong> },
        placement: "center",
        target: "body"   
      }, 
      { 
        content: <h2>User's details</h2>,
        placement: "bottom",
        target: ".user"
      },
      {
        content: <h2>Assign a role</h2>,
        placement: "bottom",
        target: ".btn-container"
      },
      {
        content: <h2>Take me to next section</h2>,
        placement: "bottom",
        target: ".navigation"
      }
    ]
  });

  return (
    <>
      <Joyride
        steps={steps}
        continuous
        hideCloseButton
        run={run}
        scrollToFirstStep
        showProgress
        showSkipButton
      />
      {/* ... */}
    </>
  );
};
```

You can see the [complete code on CodeSandbox](https://codesandbox.io/s/react-popover-libraries-c7tfcj?file=/src/components/react-joyride/TableRow.js).

## Comparison table for React popover libraries

This comparison table for all five popover libraries may be a helpful resource as you decide which option is best for your project:

<table>

<thead>

<tr>

<th>Library</th>

<th>Purpose</th>

<th>Popularity</th>

<th>Community</th>

<th>Best for</th>

</tr>

</thead>

<tbody>

<tr>

<td>Popper.js</td>

<td>Positioning engine library for popup UI elements</td>

<td>29.6K GitHub stars, 12 million weekly npm downloads</td>

<td>Active and supportive</td>

<td>Applications requiring precise positioning of UI elements</td>

</tr>

<tr>

<td>react-tiny-popover</td>

<td>Lightweight React library for creating customizable popovers</td>

<td>405 GitHub stars, 93.5K weekly npm downloads</td>

<td>Active community</td>

<td>Projects requiring a simple and customizable popover solution</td>

</tr>

<tr>

<td>react-laag</td>

<td>React library for creating position-aware UI components</td>

<td>884 GitHub stars, 138K weekly npm downloads</td>

<td>Active community</td>

<td>Projects requiring precise control over the positioning of popovers</td>

</tr>

<tr>

<td>reactjs-popup</td>

<td>React library for creating customizable popups</td>

<td>1.7k GitHub stars, 110K weekly npm downloads</td>

<td>Active community</td>

<td>Projects requiring a versatile and customizable popup solution.</td>

</tr>

<tr>

<td>React Joyride</td>

<td>React library for creating guided tours and walkthroughs</td>

<td>6k GitHub stars, 239K weekly npm downloads</td>

<td>Active community</td>

<td>Projects requiring interactive onboarding experiences</td>

</tr>

</tbody>

</table>

## Conclusion

Popovers provide an interactive and user-friendly way to enhance your React applications, but they can be challenging to implement from scratch. Popover libraries can make it easier to deal with various edge cases so your popover components look and work great on any device or screen size. 

In the vast landscape of React popover libraries, the choice boils down to your project's specific needs. Whether you're aiming for the lightweight simplicity of `reactjs-popup` and `react-tiny-popover` or the robust positioning capability of Popper.js, you can choose from a diverse range of options. 

We explored five popover and positioning libraries in this article. Experiment with these libraries, considering factors such as ease of use, customization capabilities, responsiveness, and community support, to find the popover solution that best fits your project. 

In the end, the journey through these libraries not only enhances your React skills but also empowers you to create compelling and user-friendly popover interfaces. 

See all [project demos here](https://c7tfcj.csb.app/).

---

## Get set up with LogRocket's modern React error tracking in minutes:

1. Visit https://logrocket.com/signup/ to get an app ID.
2. Install LogRocket via NPM or script tag. `LogRocket.init()` must be called client-side, not server-side.

NPM:
```bash
$ npm i --save logrocket 

// Code:

import LogRocket from 'logrocket'; 
LogRocket.init('app/id');
```
Script Tag:
```javascript
Add to your HTML:

<script src="https://cdn.lr-ingest.com/LogRocket.min.js"></script>
<script>window.LogRocket && window.LogRocket.init('app/id');</script>
```

3.(Optional) Install plugins for deeper integrations with your stack:
*   Redux middleware
*   ngrx middleware
*   Vuex plugin

[Get started now](https://lp.logrocket.com/blg/signup)