Published on

Fixing Shadcn Dialog and React-Select Position (z-index) Issues

Authors
  • Name
    Ripal & Zalak
    Twitter

Introduction

Using React-Select within a Shadcn dialog can sometimes lead to positioning and interaction issues. Specifically, the dropdown might appear visually above the dialog but remain unclickable due to z-index or pointer-events conflicts. In this article, we will explore why this happens and how to fix it.


The Problem

When integrating React-Select with Shadcn dialog:

  1. React-Select uses a portal to render its dropdown in document.body.
  2. Shadcn dialog typically has a lower z-index than the dropdown.
  3. Even with the dropdown visually above the dialog, interaction is blocked because the dialog overlays it.

Example Scenario:

import React from 'react'
import * as Dialog from '@radix-ui/react-dialog'
import Select from 'react-select'

const options = [
  { value: 'chocolate', label: 'Chocolate' },
  { value: 'strawberry', label: 'Strawberry' },
  { value: 'vanilla', label: 'Vanilla' },
]

const DialogWithReactSelect = () => (
  <Dialog.Root>
    <Dialog.Trigger asChild>
      <button className="Button">Open Dialog</button>
    </Dialog.Trigger>
    <Dialog.Portal>
      <Dialog.Overlay className="DialogOverlay" />
      <Dialog.Content className="DialogContent">
        <Dialog.Title>Select Your Flavor</Dialog.Title>
        <Select options={options} />
      </Dialog.Content>
    </Dialog.Portal>
  </Dialog.Root>
)

export default DialogWithReactSelect

Here, the React-Select dropdown is rendered inside document.body, but interaction is blocked by the dialog's pointer-events and overlay settings.


Solutions

1. Configure menuPortalTarget and pointerEvents

To ensure React-Select is both visible and interactive:

  • Use menuPortalTarget to render the dropdown inside document.body.
  • Override the pointer-events property for the portal.

Updated Code:

import Select from 'react-select'

const options = [
  { value: 'chocolate', label: 'Chocolate' },
  { value: 'strawberry', label: 'Strawberry' },
  { value: 'vanilla', label: 'Vanilla' },
]

const CustomSelect = () => (
  <Select
    options={options}
    menuPortalTarget={document.body}
    styles={{
      menuPortal: (base) => ({ ...base, zIndex: 9999, pointerEvents: 'auto' }),
    }}
  />
)

2. Adjust Dialog Overlay

Ensure the dialog overlay doesn't block interactions with elements outside of its scope by configuring pointer-events.

CSS Update:

.DialogOverlay {
  pointer-events: none;
}

.DialogContent {
  pointer-events: auto;
}

3. Use React-Select Inside Dialog

If the dropdown must render inside the dialog rather than a portal, ensure proper layering:

  • Set the dropdown's z-index higher than the dialog.

Example:

const CustomSelectInDialog = () => (
  <Select
    options={options}
    styles={{
      menu: (base) => ({ ...base, zIndex: 51 }),
    }}
  />
)

FAQs

1. Why does React-Select use a portal by default? React-Select uses a portal to avoid overflow issues and ensure the dropdown is rendered outside of parent containers that might clip it.

2. Why is pointer-events important? The pointer-events CSS property determines whether an element can receive mouse and pointer events. By default, Shadcn dialogs block pointer events for underlying elements.

3. Can I disable the portal for React-Select? Yes, but it’s not recommended as it may lead to layout and clipping issues in nested components.


Conclusion

Integrating React-Select inside Shadcn dialogs requires careful handling of z-index and pointer-events. By configuring menuPortalTarget, overriding styles, and adjusting dialog overlay properties, you can ensure the dropdown renders and functions correctly.