# How to Show Loading States with Loading.directive and Loading.service in Element UI

> Master Element UI loading states using v-loading directive and Loading.service. Learn declarative and imperative control for better user experience.

- Repository: [饿了么前端/element](https://github.com/ElemeFE/element)
- Tags: how-to-guide
- Published: 2026-03-07

---

**Element UI provides two complementary APIs for displaying loading masks—the declarative `v-loading` directive for template binding and the programmatic `Loading.service` (or `this.$loading`) factory for imperative control—both backed by the same underlying Vue component.**

The ElemeFE/element repository implements a versatile Loading system that supports both directive-based and service-based invocation patterns. Whether you prefer declarative control in Vue templates or programmatic triggers in JavaScript logic, both approaches funnel through the identical visual component defined in [`packages/loading/src/loading.vue`](https://github.com/ElemeFE/element/blob/main/packages/loading/src/loading.vue). Understanding the architectural differences between these APIs ensures you select the optimal integration pattern for your application's UX flow.

## Architectural Overview: Directive vs. Service

Element UI exposes the Loading feature through two distinct entry points that share a unified rendering layer but differ in lifecycle management and invocation style.

### Directive-Based Approach (v-loading)

The directive implementation in [`packages/loading/src/directive.js`](https://github.com/ElemeFE/element/blob/main/packages/loading/src/directive.js) registers a global `v-loading` directive that manages a `Mask` instance through standard Vue directive hooks (`bind`, `update`, `unbind`). When a bound boolean evaluates to `true`, the directive invokes `toggleLoading` to compute positioning styles and insert the DOM mask; when `false`, it triggers hide animations and destroys the instance.

**Key characteristics:**
- One `Mask` instance per element
- Modifiers (`fullscreen`, `body`, `lock`) control positioning and scroll behavior
- Attributes (`element-loading-text`, `element-loading-spinner`, `element-loading-background`, `element-loading-custom-class`) configure appearance

### Service-Based Approach (Loading.service)

The service factory exported from [`packages/loading/src/index.js`](https://github.com/ElemeFE/element/blob/main/packages/loading/src/index.js) creates a `LoadingConstructor` instance via `Vue.extend(loading.vue)`. This imperative API accepts an options object, resolves the target element, applies positioning classes, and returns a controllable instance exposing a `close()` method.

**Key characteristics:**
- Returns a programmatic instance with `close()` method
- Fullscreen instances are **global singletons**—lines 79-81 of the service explicitly return an existing `fullscreenLoading` instance if one exists
- Options (`text`, `spinner`, `background`, `customClass`, `lock`, `fullscreen`, `target`) mirror directive capabilities

## Using the v-loading Directive

Bind a boolean data property directly to the `v-loading` directive to toggle loading states declaratively on any Element UI component or DOM element.

### Basic Element Binding

```html
<template>
  <el-table
    v-loading="loading"
    :data="tableData"
    style="width: 100%">
    <!-- columns ... -->
  </el-table>

  <el-button @click="toggle">Toggle Loading</el-button>
</template>

<script>
export default {
  data() {
    return {
      loading: false,
      tableData: []
    };
  },
  methods: {
    toggle() {
      this.loading = true;
      setTimeout(() => (this.loading = false), 2000);
    }
  }
};
</script>

```

### Fullscreen and Lock Modifiers

Use modifiers to control mask positioning and prevent body scrolling. The `fullscreen` modifier creates a viewport-covering mask, while `lock` disables background scroll.

```html
<el-button
  v-loading.fullscreen.lock="fullscreenLoading"
  @click="showFullScreen">
  Full-screen Loading
</el-button>

```

```javascript
export default {
  data() {
    return { fullscreenLoading: false };
  },
  methods: {
    showFullScreen() {
      this.fullscreenLoading = true;
      setTimeout(() => (this.fullscreenLoading = false), 3000);
    }
  }
};

```

### Customization via Attributes

Configure the loading appearance using HTML attributes that the directive reads during the `bind` hook:

- `element-loading-text` – Message displayed below the spinner
- `element-loading-spinner` – Icon class for the spinner (e.g., `el-icon-loading`)
- `element-loading-background` – CSS color for the mask overlay (e.g., `rgba(0, 0, 0, 0.7)`)
- `element-loading-custom-class` – Additional CSS class applied to the mask

## Using Loading.service Programmatically

For asynchronous operations outside of template logic, use the service API to create, control, and destroy loading instances imperatively.

### Importing and Invoking the Service

Import `Loading` from `element-ui` for on-demand usage, or access `this.$loading` when Element is globally installed via `Vue.use(ElementUI)`.

```javascript
import { Loading } from 'element-ui';

export default {
  methods: {
    fetchData() {
      const loading = Loading.service({
        lock: true,
        text: 'Loading…',
        spinner: 'el-icon-loading',
        background: 'rgba(0, 0, 0, 0.7)'
      });

      setTimeout(() => {
        loading.close();
      }, 2000);
    }
  }
};

```

### Using the Vue Prototype Shortcut

When Element UI is installed globally, access the service through the Vue instance:

```javascript
export default {
  methods: {
    processFile() {
      const loading = this.$loading({ 
        text: 'Processing…',
        fullscreen: true 
      });
      
      this.asyncTask().then(() => {
        loading.close();
      });
    }
  }
};

```

### Target Resolution and Singleton Behavior

The service resolves the `target` option through `document.querySelector` if a string selector is provided. If the target is not `document.body`, the `fullscreen` option is forced to `false`. For fullscreen requests, the service checks the `fullscreenLoading` singleton reference (lines 79-81 in [`packages/loading/src/index.js`](https://github.com/ElemeFE/element/blob/main/packages/loading/src/index.js)) and returns the existing instance rather than creating duplicates.

## Core Implementation Files

Understanding the source architecture helps debug customizations and edge cases.

### packages/loading/src/loading.vue

The shared visual component that renders the spinner animation, mask overlay, and text label. Both the directive and service instantiate this component, passing configuration data via the `data` option.

### packages/loading/src/directive.js

Registers the global `v-loading` directive and implements the `toggleLoading` function that inserts or removes the mask DOM element. Handles modifier parsing and attribute extraction during the `bind` hook.

### packages/loading/src/index.js

Exports the `Loading` factory function. Manages the `fullscreenLoading` singleton, computes mask styles via `addStyle`, and applies helper classes (`el-loading-parent--relative`, `el-loading-parent--hidden`) to target parents.

### types/loading.d.ts

TypeScript definitions declaring `LoadingServiceOptions`, the `ElLoading` interface (including `service` and `$loading`), and the `ElLoadingComponent` with its `close(): void` method signature.

## Summary

- The `v-loading` directive in [`packages/loading/src/directive.js`](https://github.com/ElemeFE/element/blob/main/packages/loading/src/directive.js) provides declarative control through `bind`, `update`, and `unbind` hooks, creating a `Mask` instance per element that syncs with a boolean data property.
- `Loading.service` in [`packages/loading/src/index.js`](https://github.com/ElemeFE/element/blob/main/packages/loading/src/index.js) offers imperative control via a factory function returning a closable instance, with fullscreen instances enforced as global singletons to prevent UI conflicts.
- Both APIs consume the same underlying component from [`loading.vue`](https://github.com/ElemeFE/element/blob/main/loading.vue) and accept identical customization properties (`text`, `spinner`, `background`, `customClass`).
- Directive modifiers (`fullscreen`, `body`, `lock`) correspond directly to service options, enabling consistent positioning and scroll-locking behavior across both invocation patterns.
- Service instances must be explicitly dismissed via the returned object's `close()` method, while directive instances automatically synchronize with the bound reactive value.

## Frequently Asked Questions

### What is the difference between v-loading and Loading.service?

The `v-loading` directive ties loading state to reactive data properties in Vue templates, automatically managing DOM insertion and removal through Vue's reactivity system. `Loading.service` creates loading masks imperatively from JavaScript, making it ideal for asynchronous operations outside component templates, such as in Vuex actions or utility functions where template access is unavailable.

### How do I prevent users from interacting with the page during fullscreen loading?

Pass the `lock` option to disable body scrolling and pointer events. In the directive, append the modifier: `v-loading.fullscreen.lock="isLoading"`. In the service, include `lock: true` in the options object: `Loading.service({ fullscreen: true, lock: true })`. This adds the `el-loading-parent--hidden` class to suppress scroll and interaction on the underlying content.

### Can I display multiple fullscreen loading instances simultaneously?

No. According to the implementation in [`packages/loading/src/index.js`](https://github.com/ElemeFE/element/blob/main/packages/loading/src/index.js) (lines 79-81), fullscreen service calls check for an existing `fullscreenLoading` reference and return that instance rather than creating a new mask. This singleton design prevents conflicting overlays and ensures predictable UI behavior when multiple async operations trigger fullscreen loading states concurrently.

### How do I customize the spinner icon and background color in both APIs?

Use the `element-loading-spinner` and `element-loading-background` attributes when using the `v-loading` directive, or the `spinner` and `background` properties in the service options object. For example, directive: `<div v-loading="true" element-loading-spinner="el-icon-refresh" element-loading-background="#f3f3f3">`. Service: `Loading.service({ spinner: 'el-icon-refresh', background: '#f3f3f3' })`. Both values are passed to the underlying [`loading.vue`](https://github.com/ElemeFE/element/blob/main/loading.vue) component as props.