Kombai Logo

Building Custom Positioned Elements with React MUI Popper

When developing React applications, precisely positioning elements like tooltips, dropdowns, and custom menus can be challenging. The Material UI (MUI) Popper component provides a powerful solution for creating floating elements with pixel-perfect positioning. In this article, I'll walk you through how to leverage MUI's Popper component to build custom positioned elements that enhance your application's user experience.

Understanding MUI Popper and What We'll Build

The MUI Popper component is a wrapper around the Popper.js library, which provides precise positioning capabilities for floating elements. Unlike simpler positioning solutions, Popper handles complex scenarios including:

  • Automatic repositioning when elements would overflow the viewport
  • Maintaining position during scrolling and resizing
  • Supporting multiple placement options and alignment strategies
  • Handling complex positioning calculations efficiently

By the end of this guide, you'll understand how to:

  1. Implement basic Popper functionality with various placement options
  2. Create custom positioned components like tooltips and dropdown menus
  3. Handle edge cases with modifiers and transition effects
  4. Apply advanced positioning strategies for complex UI requirements
  5. Optimize Popper performance in real-world applications

MUI Popper Deep Dive

Before we start building, let's thoroughly understand the Popper component and its capabilities.

Component Overview

The Popper component is part of MUI's core library and provides positioning functionality without imposing any specific UI or styling. This makes it extremely flexible for building custom positioned elements. It uses the Popper.js v2 library underneath, which handles all the complex positioning calculations.

Key Props and Configuration

The Popper component accepts numerous props that control its behavior and appearance. Here are the most important ones:

PropTypeDefaultDescription
anchorElElement | Object | FunctionnullThe reference element used to position the Popper
childrenNode | Function-The content of the Popper
openBooleanfalseControls the visibility of the Popper
placementString'bottom'Position where the Popper should be displayed
transitionBooleanfalseIf true, adds transition when the Popper mounts/unmounts
disablePortalBooleanfalseDisables using Portal to render children into a new subtree
modifiersArray[]Popper.js modifiers to customize behavior
popperOptionsObjectPass options directly to Popper.js instance
popperRefRef-Ref to get access to the popper instance
keepMountedBooleanfalseAlways keep children in the DOM

Placement Options

The placement prop is particularly important as it determines where your popper will be positioned relative to the anchor element. MUI Popper supports 12 different placement options:

  • top - Element above the anchor
  • top-start - Element above, aligned with the left edge
  • top-end - Element above, aligned with the right edge
  • bottom - Element below the anchor
  • bottom-start - Element below, aligned with the left edge
  • bottom-end - Element below, aligned with the right edge
  • right - Element to the right of the anchor
  • right-start - Element to the right, aligned with the top edge
  • right-end - Element to the right, aligned with the bottom edge
  • left - Element to the left of the anchor
  • left-start - Element to the left, aligned with the top edge
  • left-end - Element to the left, aligned with the bottom edge

Controlled vs Uncontrolled Usage

The Popper component is typically used in a controlled manner, where the open state is managed externally. This gives you complete control over when the Popper is shown or hidden.

const [open, setOpen] = React.useState(false);
const anchorRef = React.useRef(null);

return (
  <>
    <Button ref={anchorRef} onClick={() => setOpen(!open)}>
      Toggle Popper
    </Button>
    <Popper open={open} anchorEl={anchorRef.current}>
      <Paper>Popper content</Paper>
    </Popper>
  </>
);

Portals and Rendering

By default, the Popper component uses React's Portal feature to render the popper content at the end of the document body. This prevents CSS overflow, z-index, or positioning context issues that might interfere with the popper's visibility. You can disable this behavior with the disablePortal prop if needed.

Modifiers System

One of the most powerful features of Popper is its modifiers system. Modifiers are plugins that can change the behavior of the positioning algorithm. For example:

  • flip - Automatically flips the popper's placement when it starts to overlap the reference element
  • preventOverflow - Prevents the popper from being positioned outside the boundary
  • offset - Offsets the popper from its reference element

Accessibility Considerations

When using Popper, you need to ensure that your custom UI components remain accessible:

  1. Use appropriate ARIA attributes (aria-expanded, aria-haspopup, etc.)
  2. Ensure keyboard navigation works correctly
  3. Add proper focus management
  4. Include appropriate role attributes

Setting Up Your Project

Let's start by setting up a React project with MUI installed. If you already have a project, you can skip to the next section.

Installing Dependencies

First, create a new React project and install the necessary dependencies:

This installs React, MUI core components, MUI icons, and the required Emotion styling dependencies.

Basic Project Structure

Let's create a simple project structure to organize our Popper examples:

Creating a Basic Popper Component

Let's start with a simple implementation to understand the core functionality of the Popper component.

Step 1: Create a Basic Popper with Click Trigger

First, let's create a basic popper that appears when a button is clicked:

In this example:

  1. We create a state variable open to control the visibility of the popper
  2. We use useRef to create a reference to the button that will trigger the popper
  3. The Popper component uses the anchorRef.current as its anchor element
  4. We set the placement to "bottom" to position the popper below the button
  5. The popper content is wrapped in a Paper component for styling

Step 2: Exploring Different Placement Options

Now, let's enhance our basic popper to demonstrate different placement options:

In this enhanced example:

  1. We added a dropdown to select from the 12 different placement options
  2. The selected placement is passed to the Popper component
  3. The UI displays the current placement for clarity
  4. We centered the button to better demonstrate the different placements

Step 3: Adding Transitions for a Smoother Experience

Let's improve our popper by adding transition effects when it appears and disappears:

This example demonstrates:

  1. Using the transition prop on the Popper component
  2. Applying MUI transition components (Fade and Grow) to the popper content
  3. Toggling between different transition effects
  4. Handling the transition props correctly

The Popper component provides a render prop with TransitionProps that we pass to our transition component. This ensures the transition is properly coordinated with the popper's visibility.

Building Practical Components with Popper

Now that we understand the basics, let's build some practical UI components using the Popper.

Creating a Custom Tooltip Component

The built-in MUI Tooltip is great, but sometimes you need more customization. Let's build our own tooltip using Popper:

This custom tooltip implementation:

  1. Uses mouse events to trigger the tooltip display
  2. Applies a fade transition for smooth appearance
  3. Includes an optional arrow that points to the anchor element
  4. Supports all placement options
  5. Uses styled components for consistent styling
  6. Implements proper offset using Popper modifiers

Building a Dropdown Menu Component

Next, let's create a dropdown menu component using Popper:

This dropdown menu implementation:

  1. Uses a button with an arrow icon as the trigger
  2. Shows a menu with icons and a divider when clicked
  3. Closes when clicking outside using ClickAwayListener
  4. Handles keyboard navigation (Escape key to close)
  5. Uses a Grow transition for a natural appearance
  6. Includes proper ARIA attributes for accessibility
  7. Has a consistent visual style with MUI's design language

Advanced Popper Techniques

Now let's explore some advanced techniques for working with the Popper component.

Virtual Element Positioning

Sometimes you need to position a popper relative to a point that isn't tied to a DOM element. Popper.js supports "virtual elements" for this purpose:

This example demonstrates:

  1. Creating a virtual element with the getBoundingClientRect method
  2. Positioning a popper at arbitrary coordinates on the screen
  3. Updating the virtual element when the position changes
  4. Handling user interactions to trigger the popper

This technique is useful for context menus, tooltips that follow the cursor, or any UI element that needs to be positioned relative to a point rather than a DOM element.

Using Popper Modifiers for Advanced Positioning

Popper.js provides modifiers that can customize the positioning behavior. Let's implement some advanced positioning with custom modifiers:

This example demonstrates:

  1. Using the offset modifier to create space between the anchor and popper
  2. Enabling/disabling the preventOverflow modifier to control boundary detection
  3. Toggling the flip modifier to control automatic placement adjustment
  4. Interactive controls to adjust modifier settings in real-time
  5. Explaining how each modifier affects the popper's behavior

Creating an Interactive Popover with Popper

Let's build a more complex popover component that can be used for forms or interactive content:

This interactive popover example:

  1. Contains a form with input fields and a submit button
  2. Handles form submission and validation
  3. Uses ClickAwayListener to close when clicking outside
  4. Includes a close button in the header
  5. Applies proper styling and spacing for a good UX
  6. Implements proper ARIA attributes for accessibility

Optimizing Popper Performance

When working with Popper in larger applications, performance can become a concern. Here are some techniques to optimize Popper usage:

Conditional Rendering and Lazy Loading

This optimization technique:

  1. Uses React's lazy loading to only load the popper content when needed
  2. Conditionally renders the popper component based on its visibility
  3. Shows a loading indicator while the content is being loaded
  4. Maintains the loaded content in the DOM for faster subsequent opens

Debouncing Position Updates

When a popper needs to update its position frequently (e.g., during scrolling), debouncing can improve performance:

This debouncing technique:

  1. Creates a debounced update function that limits how often the popper recalculates its position
  2. Uses the popperRef prop to get access to the popper instance
  3. Manually calls the popper's update method after debouncing
  4. Attaches and detaches event listeners appropriately
  5. Improves performance during scrolling and resizing

Best Practices and Common Issues

When working with MUI's Popper component, there are several best practices to follow and common issues to be aware of.

Best Practices for Using Popper

  1. Always use a controlled pattern: Manage the open state externally for better control and predictability.

  2. Handle focus management: When using Popper for interactive elements like menus, ensure proper focus management for accessibility.

  3. Use ClickAwayListener: For interactive poppers, always include a ClickAwayListener to close the popper when clicking outside.

  4. Apply proper ARIA attributes: Include appropriate ARIA attributes for accessibility, such as aria-expanded, aria-haspopup, and others as needed.

  5. Optimize rendering: Only render complex popper content when needed, and consider lazy loading for heavy content.

  6. Use transitions thoughtfully: Apply transitions for a better user experience, but keep them short (200-300ms) to maintain responsiveness.

  7. Handle edge cases: Consider edge cases like screen boundaries, scrolling, and window resizing in your implementation.

Common Issues and Their Solutions

Issue: Popper disappears when clicking inside it

This happens because the click event bubbles up and triggers the button's click handler, toggling the popper closed.

Solution:

Issue: Z-index conflicts with other elements

Sometimes the popper might appear behind other elements due to stacking context issues.

Solution:

Issue: Popper position not updating when anchor element changes size

If your anchor element changes size, the popper might not reposition automatically.

Solution:

Issue: Popper repositioning causes layout shifts

When a popper changes position (e.g., flips from bottom to top), it can cause jarring layout shifts.

Solution:

Wrapping Up

The MUI Popper component is a powerful tool for creating precisely positioned floating elements in your React applications. We've explored its core functionality, built practical UI components, and delved into advanced techniques for optimizing performance and handling edge cases.

By leveraging the Popper component, you can create sophisticated UI elements like tooltips, dropdown menus, and interactive popovers that enhance your application's user experience. Remember to consider accessibility, performance, and edge cases in your implementations to create robust and user-friendly interfaces.

Whether you're building a simple tooltip or a complex interactive popover, the techniques covered in this guide will help you create polished, professional UI components that work reliably across different devices and screen sizes.