← Blog
8 Oct 2026Vue 3Loading overlayAccessibilityAsync stateDashboard UI

loadingoverlay: A Vue Loading Overlay Guide and Package Reference

Find the loadingoverlay docs you need, compare panel and full-page loading, and build a Vue loading state with accessible status and safe async cleanup.

loadingoverlay: A Vue Loading Overlay Guide and Package Reference

TL;DR

If you searched for loadingoverlay, start with the documentation for the library in your actual stack: Mantine LoadingOverlay for Mantine React, jQuery LoadingOverlay for that plugin, or the Vue Loading Overlay repository for the Vue package. These are different implementations, not interchangeable APIs. In a Vue dashboard, we usually cover only the region that cannot be used during a request, keep progress and cancellation understandable, and remove the overlay on every completion path.

The term loading overlay describes a visual layer over content while work is in progress. LoadingOverlay can also be an exact component name, while vue-loading-overlay is a package name. This guide is a decision and implementation reference for Vue application screens, not documentation for a DOM Studio component named LoadingOverlay. The first lookup below gets you to the appropriate package source; the rest explains how to evaluate and implement the pattern.

Table of contents

Find the right loadingoverlay documentation

If your application uses Start here Confirm before adopting
Mantine in React Mantine’s LoadingOverlay reference Its parent needs relative positioning, and the documentation warns that covered controls remain keyboard-focusable.
The jQuery LoadingOverlay plugin Plugin documentation and source repository Distinguish whole-page $.LoadingOverlay(...) from element-level $(selector).LoadingOverlay(...); follow that plugin’s own installation instructions.
Vue 3 and the vue-loading-overlay package Package README and source The README maps Vue 3 to package 6.x and describes a relative-positioned container for non-full-page overlays. The owner archived this repository on March 14, 2025, so review maintenance and compatibility before a new dependency decision.
Element Plus in Vue Element Plus Loading documentation Its directive covers the bound container by default; fullscreen behavior is an explicit modifier or service choice.

These links lead to different libraries. Don’t paste a React import into a Vue file or assume that two similarly named components accept the same props. React-specific packages and Flutter overlays also appear in searches for this term, but their component and widget contracts belong to their own frameworks; choose the package documentation for the application you are building, not the most familiar-looking screenshot. Mantine’s reference, the Vue repository, and the jQuery plugin docs make those distinct contracts clear.

Package evaluation checklist: Locate the installation command and supported framework version; verify the import path and styles; check whether a container must be positioned or a portal is used; inspect the full-page option; test keyboard focus, cancellation, scroll behavior, and error recovery; then inspect source availability and maintenance history. A demo showing only a spinner does not establish any of those behaviors.

Choose a loading scope before choosing a component

Scope Good fit Prefer another pattern when
Control-level feedback A single Save or Export action is pending and the rest of the screen remains usable. The entire form genuinely cannot be edited or the action changes multiple dependent controls.
Panel or form overlay A report card is refreshing or a form is submitting, while adjacent panels and navigation should remain usable. There is no existing content to cover: a skeleton or inline placeholder can establish the incoming structure more clearly.
Application-wide overlay A short operation temporarily makes the whole workspace unusable. Only one region is waiting or the user could safely keep working elsewhere.

Our default for dashboards is smallest honest scope. A chart refresh should not block the settings sidebar. A form submission may justify covering just that form, but a Save button’s busy state may be enough when editing can safely continue. Use a determinate progress indicator only when you can report actual progress; a spinner should not imply a measured percentage. Keep the previous data visible where it is still meaningful and label it appropriately if it may be stale.

Visual comparison of inline, panel-level and full-page loading feedback

There is also a useful distinction between loading and empty. A first visit with no report rows can show a skeleton or an explanatory empty state; a subsequent refresh can retain the old rows behind a bounded overlay. For an application layout with independently scrolling navigation and workspace content, see our Vue dashboard shell guide. Its route pages own their own loading states, which helps keep one panel’s request from becoming a global shell concern.

Position and layer the overlay deliberately

For an element-level overlay, give the relevant parent a positioning context, then place the layer over that parent’s bounds. Mantine’s reference and the Vue package README both explicitly require a relatively positioned parent for their bounded examples. A generic implementation can use position: relative on the panel and position: absolute; inset: 0 on the layer. Full-page loaders use a different containing strategy; don’t achieve one by making every local overlay enormous. Mantine’s LoadingOverlay documentation and the Vue package README show their own container contracts.

Check the actual scroll and clipping boundary. overflow: hidden can clip an overlay or its contents, and an overlay positioned inside a scrolling region may cover a different visible area than intended. z-index only orders elements within their stacking contexts; raising a child’s number cannot necessarily lift it above a sibling context. Test a dropdown, sticky header, and narrow viewport while the overlay is open rather than guessing a universal z-index. See MDN’s overflow reference and stacking context explanation.

The video below demonstrates the visual idea of a transparent layer over a div. Treat it as a positioning illustration, not as a substitute for the keyboard, status, and async-state checks that follow.

Make the busy state understandable and operable

An overlay can stop pointer clicks without preventing a keyboard user from tabbing into the covered controls. Mantine explicitly warns about this in its own LoadingOverlay documentation. If the underlying region truly must be unavailable, make only that content subtree inert or manage its interactive controls individually. The HTML inert attribute removes descendant controls from focus and the accessibility tree, so never put your status message or an essential Cancel button inside the inert subtree. Keep unaffected navigation outside it. Mantine’s keyboard warning and MDN’s inert reference explain the difference.

aria-busy="true" identifies a region being updated; it does not disable controls, announce a useful message by itself, or replace interaction management. Put a short visible message such as “Refreshing report” in a persistent role="status" region outside the busy subtree. The ARIA busy reference describes update semantics, while W3C’s status-message technique describes polite announcements. Avoid repeatedly replacing status text with an animated countdown. Ensure a user can still reach any cancellation control by keyboard, and leave focus somewhere meaningful when the overlay closes.

Form loading state with status, reachable cancel control and error area outside overlay

A visual spinner is optional; a meaningful state is not. We design for these transitions:

Event UI outcome
Start Mark the relevant region busy; show a short status; prevent only interactions that would be unsafe.
Success Replace stale data, clear busy state, and give a concise completion message when useful.
Error Remove the overlay, preserve usable content where possible, explain the failure, and offer retry.
Cancel Abort work when supported; otherwise clearly distinguish hiding the indicator from actually stopping the operation.
Timeout or stalled request Stop indefinite blocking according to your product’s timeout policy and provide a recovery path.

For form submissions, keep validation and server errors associated with their fields once the pending state ends. Our Vue form components guide covers that form-level error contract; a loading layer should not erase the values someone has already entered.

Vue 3 example: refresh one report panel

This example uses plain Vue 3 and browser APIs, not a DOM Studio LoadingOverlay component. It assumes a Vue single-file-component project and a GET /api/reports endpoint returning JSON shaped like { "items": [{ "id": "r1", "name": "Weekly report" }] }. Replace that illustrative endpoint and validate the actual response contract in your application. The previous rows stay visible while the panel updates; the button and status message sit outside the busy region.

<script setup>
import { onBeforeUnmount, ref } from 'vue'

const reports = ref([])
const loading = ref(false)
const message = ref('')
let controller = null
let requestId = 0

async function refresh() {
  if (loading.value) return
  const id = ++requestId
  const request = new AbortController()
  controller = request
  loading.value = true
  message.value = 'Refreshing reports'

  try {
    const response = await fetch('/api/reports', { signal: request.signal })
    if (!response.ok) throw new Error('Request failed')
    const data = await response.json()
    if (id !== requestId) return
    if (!Array.isArray(data.items)) throw new Error('Unexpected response')
    reports.value = data.items
    message.value = 'Reports updated'
  } catch (error) {
    if (id !== requestId) return
    message.value = error.name === 'AbortError'
      ? 'Refresh canceled'
      : 'Could not refresh reports. Try again.'
  } finally {
    if (id === requestId) {
      loading.value = false
      controller = null
    }
  }
}

function cancel() {
  if (!loading.value) return
  ++requestId
  controller?.abort()
  controller = null
  loading.value = false
  message.value = 'Refresh canceled'
}

onBeforeUnmount(() => {
  ++requestId
  controller?.abort()
})
</script>

<template>
  <div class="report-workspace">
    <div class="report-actions">
      <button type="button" :aria-disabled="loading" @click="refresh">
        Refresh reports
      </button>
      <button v-if="loading" type="button" @click="cancel">Cancel refresh</button>
    </div>
    <p role="status" aria-atomic="true">{{ message }}</p>

    <section class="report-panel" aria-label="Reports" :aria-busy="loading">
      <div :inert="loading">
        <h2>Reports</h2>
        <ul>
          <li v-for="report in reports" :key="report.id">{{ report.name }}</li>
        </ul>
        <p v-if="!reports.length">No reports loaded yet.</p>
      </div>
      <div v-if="loading" class="report-overlay" aria-hidden="true">
        <span class="report-spinner"></span>
        <span>Refreshing reports</span>
      </div>
    </section>
  </div>
</template>

<style scoped>
.report-actions { display: flex; gap: 0.75rem; }
.report-panel { position: relative; min-height: 10rem; }
.report-overlay {
  position: absolute;
  inset: 0;
  z-index: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.75rem;
  background: rgb(255 255 255 / 0.85);
}
.report-spinner {
  width: 1.25rem;
  height: 1.25rem;
  border: 2px solid currentColor;
  border-right-color: transparent;
  border-radius: 50%;
  animation: spin 0.9s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
  .report-spinner { animation: none; }
}
</style>

Vue documents ref() for reactive state and onBeforeUnmount() for cleanup; the browser’s AbortController cancels the fetch when possible. The request ID also guards against a stale response updating a component after cancellation. A timeout, API-specific validation, and authorization are application-specific and omitted. If your interface permits overlapping refreshes from other entry points, use a shared request policy rather than assuming one button’s guard covers them all. Consult Vue reactivity, Vue lifecycle hooks, and MDN’s fetch cancellation guidance.

Quick checks before shipping

  1. Refresh a single dashboard card: can the neighboring card and navigation still be used?
  2. Tab while the overlay is visible: are covered controls skipped and Cancel reachable?
  3. Simulate an HTTP failure, a malformed response, cancellation, and route removal: does every path clear or safely abandon the busy state?
  4. Repeat the test with a clipped container, a sticky header, mobile width, and reduced-motion preferences.
  5. Confirm that status text describes what is happening, while success and failure remain visible long enough to understand.

For DOM Studio layouts, begin with the verified Application Layout example as a workspace reference, then implement the loading behavior in the route or panel that owns the request. That example is not a LoadingOverlay component or package entry point.

Sources

Recommended Reads