Skip to content

[NcModal] Nested modal is sometimes rendered behind containg modalΒ #8201

@odzhychko

Description

@odzhychko

Occasionally a modal that is opened from inside another modal is not shown on top.

This happens when, in between opening the modals, other components also manipulate the children of the <body> element.

Originally observed in nextcloud/calendar#7822

Demo

Screencast.From.2026-02-12.18-25-21.webm

Steps to Reproduce

Given the following setup:

<template>
  <div>
    <NcActions
      @opened="() => (popoverRemovedFromDom = false)"
      @close="onPopoverClose"
    >
      <NcActionButton>Edit</NcActionButton>
      <NcActionButton>Delete</NcActionButton>
    </NcActions>
    <NcButton @click="() => (outerModal = true)">Show outer modal</NcButton>
    <NcModal
      v-if="outerModal"
      @close="() => (outerModal = false)"
      name="Outer Modal"
      size="large"
    >
      <div class="modal__content">
        <div v-if="!popoverRemovedFromDom">
          The button will be enabled automatically in a few seconds. We are
          waiting until the popover is removed from the DOM.
        </div>
        <NcButton
          :disabled="!popoverRemovedFromDom"
          @click="() => (nestedModal = true)"
          >Show nested modal</NcButton
        >
        <NcModal
          v-if="nestedModal"
          @close="() => (nestedModal = false)"
          name="Nested Modal"
          size="small"
        >
          <div class="modal__content" style="height: 256px">
            Nested modal content
          </div>
        </NcModal>
      </div>
    </NcModal>
  </div>
</template>
<script>
export default {
  data() {
    return {
      outerModal: false,
      nestedModal: false,
      popoverRemovedFromDom: true,
    };
  },
  methods: {
    onPopoverClose() {
      // We used 'close` event, because 'closed' is currently not triggered in v9.
      // See https://github.com/nextcloud-libraries/nextcloud-vue/issues/8200
      // Also in v8 `closed` does not guarantee that the popover was removed from the DOM.
      // So in any case, we just wait a bit to guarantee that the popover was removed, which triggers the bug for modals in v8.
      setTimeout(() => {
        this.popoverRemovedFromDom = true;
      }, 5000);
    },
  },
};
</script>
<style scoped>
.modal__content {
  margin: 64px;
}
</style>
  1. Click on the menu indicator to open the popover
  2. Click on "Show outer modal"
  3. Wait until the button "Show nested modal" is enabled
  4. Click on "Show nested modal"

Actual Behavior

"Outer modal" is on top of "Nested Modal"

Expected Behavior

"Nested Modal" shown on top of "Outer modal".

closed event being emitted.

Environment

Notes

This is caused by how the modals are appended at:

document.body.insertBefore(this.$el, document.body.lastChild)

v9 uses <Teleport> from Vue 3 which does not have this issue.

Besides the popover from <NcActions> other components might also trigger this behavior if they append and remove nodes to document.body.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions