# How Astryx Popover and useLayer Hooks Enable Precise Overlay Positioning

> Learn how Astryx Popover and useLayer hooks in React precisely position overlays using CSS anchor positioning and the native dialog API. Track anchor changes automatically.

- Repository: [Meta/astryx](https://github.com/facebook/astryx)
- Tags: how-to-guide
- Published: 2026-08-06

---

**Astryx Popover is a React wrapper around the native `<dialog>` Popover API that uses CSS anchor positioning and the `useLayer` hook to render floating panels above other content while automatically tracking anchor element changes.**

The `facebook/astryx` repository provides a modern overlay system that combines native browser APIs with React hooks. Understanding how the **Astryx Popover** and **useLayer** hooks work for overlay positioning reveals a performant approach to building tooltips, menus, and dropdowns that stay synchronized with their trigger elements.

## Anchor-Based Positioning with CSS Anchor Positioning

The **Popover** component establishes a relationship between a trigger element and its floating panel through an **anchor ref**. You pass an `anchorRef` to the Popover, which the component uses to compute placement relative to the trigger.

Internally, Astryx leverages the browser's **CSS anchor positioning** properties—`position-anchor`, `position-anchor-size`, and `position-anchor-gap`—to handle calculations. This native CSS approach means the overlay automatically tracks size and position changes of the anchor without requiring JavaScript resize observers or expensive re-calculations. According to the source code in [`packages/core/src/Tooltip/Tooltip.tsx`](https://github.com/facebook/astryx/blob/main/packages/core/src/Tooltip/Tooltip.tsx) at line 161, the Tooltip component (which utilizes Popover) relies on these CSS anchor positioning properties to maintain alignment.

## Native Popover API Integration

Rather than implementing custom show/hide logic, the **Popover** component calls native browser methods directly. The hook invokes `element.showPopover()` to display the overlay and `element.hidePopover()` to dismiss it, targeting either the anchor element or the panel element itself.

This native integration provides **light dismiss** functionality automatically—users can close the overlay by clicking outside or pressing the Escape key without additional event listeners. The component also adds appropriate ARIA attributes such as `aria-haspopup="true"` and `role="dialog"` when applicable, as documented in [`packages/lab/src/Tour/TourStep.tsx`](https://github.com/facebook/astryx/blob/main/packages/lab/src/Tour/TourStep.tsx) at lines 216-218.

In the test suite, these native calls are mocked because JSDOM does not implement the Popover API. The mock implementation in [`packages/core/src/Tooltip/Tooltip.test.tsx`](https://github.com/facebook/astryx/blob/main/packages/core/src/Tooltip/Tooltip.test.tsx) (lines 22-27) replaces `HTMLElement.prototype.showPopover` and `hidePopover` to enable reliable unit testing.

## Layer Promotion via the useLayer Hook

To ensure overlays render above other content—even when anchors live inside scrolling containers or nested component trees—Astryx provides the **useLayer** hook. This hook moves the Popover's panel into a **top-layer portal**, creating a dedicated DOM node with a high `z-index` that exists outside the normal stacking context.

The hook also maintains a **layer stack** that tracks multiple simultaneous overlays. This prevents visual conflicts when multiple popovers, tooltips, or menus are active concurrently. You can see this implementation in [`packages/core/src/TopNav/TopNavMenu.tsx`](https://github.com/facebook/astryx/blob/main/packages/core/src/TopNav/TopNavMenu.tsx) at lines 27-34, where `usePopover` internally calls `useLayer` to promote the dropdown menu to the top layer.

## Accessibility and ARIA Management

The Popover component handles accessibility concerns automatically through its native API integration. When appropriate, it applies `aria-haspopup="true"` to anchor elements and assigns `role="dialog"` to the overlay panel.

Because the component uses the browser's native Popover API, it inherits built-in accessibility behaviors including focus management and light dismiss. This eliminates the need for additional boilerplate code to handle keyboard navigation or click-outside detection, keeping the markup clean while maintaining WCAG compliance.

## Implementation Examples

### Basic Popover Usage

Here is a minimal implementation attaching a Popover to a button element:

```tsx
import {Popover} from '@astryxdesign/core/Popover';
import {useRef} from 'react';

function Example() {
  const buttonRef = useRef<HTMLButtonElement>(null);

  return (
    <>
      <Popover
        anchorRef={buttonRef}
        placement="bottom"
      >
        <div>Hello world</div>
      </Popover>
      <button ref={buttonRef}>Hover me</button>
    </>
  );
}

```

### Underlying usePopover Hook Structure

The `usePopover` hook in [`packages/core/src/Popover/usePopover.ts`](https://github.com/facebook/astryx/blob/main/packages/core/src/Popover/usePopover.ts) demonstrates the internal mechanics:

```tsx
export function usePopover({anchorRef, placement, onShow, onHide}) {
  const layer = useLayer();
  const open = () => {
    const el = anchorRef.current;
    if (el?.showPopover) el.showPopover();
    onShow?.();
  };
  const close = () => {
    const el = anchorRef.current;
    if (el?.hidePopover) el.hidePopover();
    onHide?.();
  };
  return {open, close, layer};
}

```

### Complex Component Implementation

For a real-world example, the Chat emoji picker in [`packages/lab/src/Chat/ChatEmojiPicker.tsx`](https://github.com/facebook/astryx/blob/main/packages/lab/src/Chat/ChatEmojiPicker.tsx) demonstrates Popover usage within a complex UI:

```tsx
import {Popover} from '@astryxdesign/core/Popover';
import {useRef} from 'react';

export function ChatEmojiPicker() {
  const triggerRef = useRef<HTMLButtonElement>(null);
  return (
    <>
      <Popover anchorRef={triggerRef} placement="top">
        <EmojiGrid />
      </Popover>
      <button ref={triggerRef}>😀</button>
    </>
  );
}

```

## Summary

- **CSS anchor positioning** handles overlay placement natively, eliminating JavaScript layout calculations for better performance.
- The **useLayer** hook creates a top-layer portal with high `z-index` and manages stacking for multiple simultaneous overlays.
- **Native Popover API** methods (`showPopover`/`hidePopover`) provide built-in light dismiss and accessibility features.
- The `anchorRef` contract connects trigger elements to their panels, with automatic synchronization of size and position changes.
- Testing requires mocking the Popover API in JSDOM environments, as demonstrated in the Tooltip test suite.

## Frequently Asked Questions

### What is the relationship between Popover and useLayer in Astryx?

The **Popover** component uses the **useLayer** hook internally to manage DOM placement and z-index stacking. While Popover handles the anchor positioning logic and native API calls, `useLayer` creates the top-layer portal that ensures the overlay renders above other content regardless of parent container stacking contexts.

### How does Astryx Popover handle positioning without JavaScript calculations?

Astryx delegates positioning to the browser's **CSS anchor positioning** specification. By setting CSS properties like `position-anchor` and `position-anchor-gap` on the overlay element, the browser automatically calculates and updates the position relative to the anchor element. This approach, visible in [`packages/core/src/Tooltip/Tooltip.tsx`](https://github.com/facebook/astryx/blob/main/packages/core/src/Tooltip/Tooltip.tsx), avoids the performance costs of JavaScript-based positioning libraries.

### Why does Astryx use the native Popover API instead of a custom implementation?

The native Popover API provides **light dismiss** (click-outside and Escape key handling), proper focus management, and accessibility features without additional code. Using `showPopover()` and `hidePopover()` reduces bundle size and ensures consistent behavior across browsers that support the API, while the `useLayer` hook handles the rendering layer for older browsers or complex stacking scenarios.

### How do you test components that use Astryx Popover in JSDOM?

Since JSDOM does not implement the Popover API, you must mock the native methods on `HTMLElement.prototype`. The test suite in [`packages/core/src/Tooltip/Tooltip.test.tsx`](https://github.com/facebook/astryx/blob/main/packages/core/src/Tooltip/Tooltip.test.tsx) (lines 22-27) provides a reference implementation that mocks `showPopover` and `hidePopover` to verify component behavior without requiring a real browser environment.