# Element UI User Guide

## 1. Quick Start

Element UI is a Vue 2.x component library providing 67+ production-ready UI components for building web applications. Developed by the Ele.me frontend team, it offers a complete design system with consistent styling, internationalization, and theme customization.

**Key features:**

- 67+ components covering forms, data display, navigation, feedback, and layout
- Built-in i18n with 50+ language packs
- Customizable theme system via SCSS variables
- TypeScript type definitions included
- Vue Router integration for navigation components
- Service-based components (Message, Notification, MessageBox, Loading)
- Responsive grid system with 5 breakpoints
- Form validation powered by async-validator

**Prerequisites:** Node.js 10+, Vue 2.5.17+

**Install and verify:**

```bash
npm install element-ui
```

```javascript
import Vue from 'vue';
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';

Vue.use(ElementUI);

new Vue({
  el: '#app',
  render: h => h(App)
});
```

**What to try next:** See Installation for all methods, Configuration for global options, or jump to Core Usage for component examples.

## 2. Installation

### System Requirements

| Requirement | Version |
|---|---|
| Node.js | 10+ |
| Vue | ^2.5.17 |
| npm / yarn | Any recent |

### Install Methods

=== "npm"
    ```bash
    npm install element-ui
    ```

=== "yarn"
    ```bash
    yarn add element-ui
    ```

=== "CDN"
    ```html
    <!-- CSS -->
    <link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css">
    <!-- JS -->
    <script src="https://unpkg.com/vue@2/dist/vue.js"></script>
    <script src="https://unpkg.com/element-ui/lib/index.js"></script>
    ```

> [!TIP]
> The CDN approach is the fastest way to prototype. Element UI auto-installs itself when it detects `window.Vue`.

### Full Import

Register all components globally:

```javascript
import Vue from 'vue';
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';

Vue.use(ElementUI);
```

### Partial Import

Import only the components you need to reduce bundle size. Use `babel-plugin-component` for automatic style imports:

```bash
npm install babel-plugin-component --save-dev
```

Configure `.babelrc`:

```json
{
  "plugins": [
    ["component", {
      "libraryName": "element-ui",
      "styleLibraryName": "theme-chalk"
    }]
  ]
}
```

Then import individual components:

```javascript
import { Button, Select, Table } from 'element-ui';

Vue.component(Button.name, Button);
Vue.component(Select.name, Select);
Vue.component(Table.name, Table);
```

> [!IMPORTANT]
> When using partial imports, you must still manually register service components if needed: `Vue.prototype.$message = Message;` and `Vue.use(Loading.directive);`.

### Post-Install Verification

```javascript
import ElementUI from 'element-ui';
console.log(ElementUI.version);
```

```text
2.15.14
```

### Install Troubleshooting

### Error: `node-sass` build failure

**Cause:** `node-sass` is a devDependency of Element UI's theme package and can fail on newer Node versions.

**Solution:**
1. Use `npm install --ignore-scripts` if you don't need to rebuild themes
2. Or install the pre-built CSS from `element-ui/lib/theme-chalk/index.css`

### Error: `Module not found: element-ui`

**Cause:** Package not installed or incorrect import path.

**Solution:**
```bash
npm install element-ui --save
```

### Error: Components render without styles

**Cause:** Missing CSS import.

**Solution:**
```javascript
import 'element-ui/lib/theme-chalk/index.css';
```

### Error: `Vue is not a constructor` or `Cannot read property 'use' of undefined`

**Cause:** Vue 3 project trying to use Element UI (which requires Vue 2).

**Solution:** Element UI only supports Vue 2.x. For Vue 3, use Element Plus instead.

## 3. Configuration

### Global Configuration

Pass options to `Vue.use()` to configure all components:

```javascript
Vue.use(ElementUI, {
  size: 'small',    // Component size: 'medium' | 'small' | 'mini'
  zIndex: 3000,     // Starting z-index for popups (default: 2000)
  locale: enLocale, // Language pack
  i18n: fn          // Custom i18n handler
});
```

| Option | Type | Default | Description |
|---|---|---|---|
| `size` | `string` | `''` | Global component size: `'medium'`, `'small'`, `'mini'` |
| `zIndex` | `number` | `2000` | Starting z-index for popup-based components |
| `locale` | `object` | zh-CN | Language pack object |
| `i18n` | `function` | `null` | Custom i18n translation function |

> [!NOTE]
> The global `size` and `zIndex` are stored on `Vue.prototype.$ELEMENT` and can be accessed from any component instance.

### Internationalization (i18n)

Element UI defaults to Chinese (zh-CN). Switch to English or other languages:

```javascript
import lang from 'element-ui/lib/locale/lang/en';
import locale from 'element-ui/lib/locale';

locale.use(lang);
```

**With vue-i18n:**

```javascript
import Vue from 'vue';
import VueI18n from 'vue-i18n';
import ElementUI from 'element-ui';
import enLocale from 'element-ui/lib/locale/lang/en';
import zhLocale from 'element-ui/lib/locale/lang/zh-CN';

Vue.use(VueI18n);

const i18n = new VueI18n({
  locale: 'en',
  messages: {
    en: { ...enLocale },
    zh: { ...zhLocale }
  }
});

Vue.use(ElementUI, { i18n: (key, value) => i18n.t(key, value) });
```

> [!TIP]
> Element UI includes 50+ language packs in `element-ui/lib/locale/lang/`. Check the `src/locale/lang/` directory for the full list.

For full configuration reference, see the Admin Guide.

## 4. Core Usage

### Basic Components

#### Button

Buttons support types, sizes, icons, loading state, and grouping:

```html
<el-button>Default</el-button>
<el-button type="primary">Primary</el-button>
<el-button type="success" icon="el-icon-check">Success</el-button>
<el-button type="danger" round>Rounded</el-button>
<el-button type="warning" circle icon="el-icon-star-off"></el-button>
<el-button :loading="true">Loading</el-button>
```

| Prop | Type | Default | Description |
|---|---|---|---|
| `type` | `string` | — | `'primary'`, `'success'`, `'warning'`, `'danger'`, `'info'`, `'text'` |
| `size` | `string` | — | `'medium'`, `'small'`, `'mini'` |
| `plain` | `boolean` | `false` | Plain style |
| `round` | `boolean` | `false` | Round corners |
| `circle` | `boolean` | `false` | Circle shape |
| `loading` | `boolean` | `false` | Show loading spinner |
| `disabled` | `boolean` | `false` | Disable the button |
| `icon` | `string` | — | Icon class name |
| `nativeType` | `string` | `'button'` | Native `type`: `'button'`, `'submit'`, `'reset'` |

#### Icon

Element UI provides a built-in icon set. Use icon names with the `el-icon-` prefix:

```html
<i class="el-icon-edit"></i>
<i class="el-icon-share"></i>
<i class="el-icon-delete"></i>

<!-- In buttons -->
<el-button icon="el-icon-search">Search</el-button>
```

#### Link

Text hyperlink component:

```html
<el-link href="https://example.com" target="_blank" type="primary">Link</el-link>
<el-link :underline="false">No Underline</el-link>
<el-link disabled>Disabled</el-link>
```

### Layout

#### Container System

Build page layouts with semantic container components:

```html
<el-container>
  <el-header height="60px">Header</el-header>
  <el-container>
    <el-aside width="200px">Sidebar</el-aside>
    <el-main>Content</el-main>
  </el-container>
  <el-footer height="60px">Footer</el-footer>
</el-container>
```

> [!NOTE]
> When `<el-container>` contains `<el-header>` or `<el-footer>`, its children arrange vertically. Otherwise they arrange horizontally. Override with the `direction` prop.

#### Grid System (Row / Col)

A responsive 24-column grid:

```html
<el-row :gutter="20">
  <el-col :span="6"><div>col-6</div></el-col>
  <el-col :span="6"><div>col-6</div></el-col>
  <el-col :span="6"><div>col-6</div></el-col>
  <el-col :span="6"><div>col-6</div></el-col>
</el-row>

<!-- Responsive -->
<el-row>
  <el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="4">
    <div>Responsive column</div>
  </el-col>
</el-row>
```

**Row props:**

| Prop | Type | Default | Description |
|---|---|---|---|
| `gutter` | `number` | `0` | Grid spacing in pixels |
| `type` | `string` | — | Set to `'flex'` for flexbox mode |
| `justify` | `string` | — | Flex horizontal alignment |
| `align` | `string` | — | Flex vertical alignment |

**Col responsive breakpoints:**

| Prop | Breakpoint |
|---|---|
| `xs` | <768px |
| `sm` | ≥768px |
| `md` | ≥992px |
| `lg` | ≥1200px |
| `xl` | ≥1920px |

### Form Components

#### Input

```html
<el-input v-model="text" placeholder="Enter text"></el-input>

<!-- Textarea -->
<el-input type="textarea" :rows="4" v-model="content"></el-input>

<!-- With icon -->
<el-input prefix-icon="el-icon-search" v-model="search"></el-input>

<!-- Clearable -->
<el-input v-model="text" clearable></el-input>

<!-- Password toggle -->
<el-input v-model="password" show-password></el-input>

<!-- Word limit -->
<el-input v-model="text" maxlength="100" show-word-limit></el-input>
```

| Prop | Type | Default | Description |
|---|---|---|---|
| `type` | `string` | `'text'` | Input type: `'text'`, `'textarea'`, or native types |
| `v-model` | `string/number` | — | Binding value |
| `placeholder` | `string` | — | Placeholder text |
| `clearable` | `boolean` | `false` | Show clear button |
| `show-password` | `boolean` | `false` | Toggle password visibility |
| `show-word-limit` | `boolean` | `false` | Show word count (requires `maxlength`) |
| `disabled` | `boolean` | `false` | Disable input |
| `size` | `string` | — | `'medium'`, `'small'`, `'mini'` |
| `prefix-icon` | `string` | — | Prefix icon class |
| `suffix-icon` | `string` | — | Suffix icon class |
| `autosize` | `boolean/object` | `false` | Auto-resize textarea: `true` or `{ minRows: 2, maxRows: 6 }` |

**Slots:** `prefix`, `suffix`, `prepend`, `append`

#### InputNumber

```html
<el-input-number v-model="num" :min="1" :max="100" :step="2"></el-input-number>
```

#### Select

```html
<el-select v-model="value" placeholder="Select">
  <el-option
    v-for="item in options"
    :key="item.value"
    :label="item.label"
    :value="item.value">
  </el-option>
</el-select>

<!-- Multiple selection -->
<el-select v-model="values" multiple placeholder="Select">
  <el-option v-for="item in options" :key="item.value"
    :label="item.label" :value="item.value">
  </el-option>
</el-select>

<!-- Filterable -->
<el-select v-model="value" filterable placeholder="Search">
  <el-option v-for="item in options" :key="item.value"
    :label="item.label" :value="item.value">
  </el-option>
</el-select>

<!-- Remote search -->
<el-select v-model="value" filterable remote :remote-method="search"
  :loading="loading" placeholder="Search">
  <el-option v-for="item in results" :key="item.value"
    :label="item.label" :value="item.value">
  </el-option>
</el-select>
```

| Prop | Type | Default | Description |
|---|---|---|---|
| `v-model` | `any` | — | Binding value |
| `multiple` | `boolean` | `false` | Enable multi-select |
| `filterable` | `boolean` | `false` | Enable search filtering |
| `remote` | `boolean` | `false` | Enable remote search |
| `remote-method` | `function` | — | Remote search function |
| `clearable` | `boolean` | `false` | Show clear button |
| `allow-create` | `boolean` | `false` | Allow creating new items (requires `filterable`) |
| `multiple-limit` | `number` | `0` | Max selections (0 = unlimited) |
| `collapse-tags` | `boolean` | `false` | Collapse tags in multi-select |
| `default-first-option` | `boolean` | `false` | Select first match on Enter |

> [!TIP]
> Group options with `<el-option-group>` by wrapping related `<el-option>` elements and setting a `label` prop on the group.

#### Radio and Checkbox

```html
<!-- Radio -->
<el-radio-group v-model="radio">
  <el-radio :label="1">Option A</el-radio>
  <el-radio :label="2">Option B</el-radio>
</el-radio-group>

<!-- Radio buttons -->
<el-radio-group v-model="radio">
  <el-radio-button label="Option A"></el-radio-button>
  <el-radio-button label="Option B"></el-radio-button>
</el-radio-group>

<!-- Checkbox -->
<el-checkbox-group v-model="checked">
  <el-checkbox label="Option A"></el-checkbox>
  <el-checkbox label="Option B"></el-checkbox>
</el-checkbox-group>
```

#### Switch

```html
<el-switch v-model="active"></el-switch>

<!-- Custom values and colors -->
<el-switch v-model="value"
  active-color="#13ce66"
  inactive-color="#ff4949"
  active-value="on"
  inactive-value="off">
</el-switch>
```

#### Slider

```html
<el-slider v-model="value"></el-slider>

<!-- Range -->
<el-slider v-model="range" range :min="0" :max="100"></el-slider>

<!-- With input box -->
<el-slider v-model="value" show-input></el-slider>
```

#### DatePicker and TimePicker

```html
<!-- Date -->
<el-date-picker v-model="date" type="date" placeholder="Pick a date">
</el-date-picker>

<!-- Date range -->
<el-date-picker v-model="dateRange" type="daterange"
  start-placeholder="Start" end-placeholder="End">
</el-date-picker>

<!-- DateTime -->
<el-date-picker v-model="datetime" type="datetime"
  placeholder="Pick date and time">
</el-date-picker>

<!-- Time picker -->
<el-time-picker v-model="time" placeholder="Pick time">
</el-time-picker>

<!-- Time select (fixed intervals) -->
<el-time-select v-model="time" placeholder="Pick time"
  :picker-options="{ start: '08:00', step: '00:30', end: '18:00' }">
</el-time-select>
```

| DatePicker `type` | Description |
|---|---|
| `date` | Single date |
| `daterange` | Date range |
| `datetime` | Date with time |
| `datetimerange` | DateTime range |
| `week` | Week picker |
| `month` | Month picker |
| `monthrange` | Month range |
| `year` | Year picker |

> [!WARNING]
> The `firstDayOfWeek` option in `picker-options` has known issues when used with `type="week"` (see issue #23126). Test thoroughly if your application requires non-default week start days.

#### Upload

```html
<el-upload action="https://api.example.com/upload"
  :on-success="handleSuccess"
  :on-error="handleError"
  :limit="3"
  :on-exceed="handleExceed">
  <el-button type="primary">Click to Upload</el-button>
  <div slot="tip" class="el-upload__tip">Only jpg/png files, max 500KB</div>
</el-upload>

<!-- Drag-and-drop -->
<el-upload action="https://api.example.com/upload" drag multiple>
  <i class="el-icon-upload"></i>
  <div class="el-upload__text">Drop files here or <em>click to upload</em></div>
</el-upload>
```

| Prop | Type | Default | Description |
|---|---|---|---|
| `action` | `string` | **required** | Upload endpoint URL |
| `multiple` | `boolean` | `false` | Allow multiple files |
| `drag` | `boolean` | `false` | Enable drag-and-drop |
| `accept` | `string` | — | Accepted file types |
| `limit` | `number` | — | Max file count |
| `auto-upload` | `boolean` | `true` | Upload immediately on select |
| `list-type` | `string` | `'text'` | `'text'`, `'picture'`, `'picture-card'` |
| `headers` | `object` | — | Request headers |
| `data` | `object` | — | Additional form data |
| `with-credentials` | `boolean` | `false` | Send cookies |
| `http-request` | `function` | — | Override default upload behavior |

#### Cascader

Hierarchical selection for data with parent-child relationships:

```html
<el-cascader v-model="value" :options="options" :props="{ expandTrigger: 'hover' }">
</el-cascader>

<!-- Filterable -->
<el-cascader v-model="value" :options="options" filterable></el-cascader>
```

#### ColorPicker

```html
<el-color-picker v-model="color"></el-color-picker>
<el-color-picker v-model="color" show-alpha></el-color-picker>
```

#### Transfer

Dual-list selection for moving items between two panels:

```html
<el-transfer v-model="selected" :data="data"
  :titles="['Available', 'Selected']" filterable>
</el-transfer>
```

#### Rate

```html
<el-rate v-model="score"></el-rate>
<el-rate v-model="score" show-text :texts="['Bad','Poor','Normal','Good','Great']"></el-rate>
```

### Form Validation

Combine `<el-form>` with validation rules powered by async-validator:

```html
<el-form :model="form" :rules="rules" ref="form" label-width="120px">
  <el-form-item label="Name" prop="name">
    <el-input v-model="form.name"></el-input>
  </el-form-item>
  <el-form-item label="Email" prop="email">
    <el-input v-model="form.email"></el-input>
  </el-form-item>
  <el-form-item>
    <el-button type="primary" @click="submitForm">Submit</el-button>
    <el-button @click="resetForm">Reset</el-button>
  </el-form-item>
</el-form>
```

```javascript
export default {
  data() {
    return {
      form: { name: '', email: '' },
      rules: {
        name: [
          { required: true, message: 'Name is required', trigger: 'blur' },
          { min: 3, max: 50, message: 'Length must be 3-50', trigger: 'blur' }
        ],
        email: [
          { required: true, message: 'Email is required', trigger: 'blur' },
          { type: 'email', message: 'Invalid email format', trigger: ['blur', 'change'] }
        ]
      }
    };
  },
  methods: {
    submitForm() {
      this.$refs.form.validate(valid => {
        if (valid) { /* submit */ }
      });
    },
    resetForm() {
      this.$refs.form.resetFields();
    }
  }
};
```

**Form methods:**

| Method | Description |
|---|---|
| `validate(callback)` | Validate entire form. Callback receives `(valid, failedFields)` |
| `validateField(props, callback)` | Validate specific field(s) |
| `resetFields()` | Reset all fields and clear validation |
| `clearValidate(props?)` | Clear validation messages |

> [!IMPORTANT]
> The `prop` attribute on `<el-form-item>` must match a key in the form's `:model` object. Nested paths use colon notation: `prop="address:city"`.

### Data Display

#### Table

```html
<el-table :data="tableData" style="width: 100%">
  <el-table-column prop="name" label="Name" width="180"></el-table-column>
  <el-table-column prop="email" label="Email"></el-table-column>
  <el-table-column prop="date" label="Date" sortable></el-table-column>
  <el-table-column label="Actions">
    <template slot-scope="scope">
      <el-button size="mini" @click="handleEdit(scope.row)">Edit</el-button>
      <el-button size="mini" type="danger" @click="handleDelete(scope.row)">Delete</el-button>
    </template>
  </el-table-column>
</el-table>
```

**Key Table features:**

| Feature | How to enable |
|---|---|
| Striped rows | `stripe` prop on `<el-table>` |
| Border | `border` prop |
| Fixed height + scroll | `height` or `max-height` prop |
| Fixed columns | `fixed` prop on `<el-table-column>` (`true`, `'left'`, `'right'`) |
| Sorting | `sortable` prop on column (`true` or `'custom'`) |
| Filtering | `filters` + `filter-method` props on column |
| Selection | Column with `type="selection"` |
| Expand row | Column with `type="expand"` + scoped slot |
| Summary row | `show-summary` prop on table |
| Row index | Column with `type="index"` |
| Row highlight | `highlight-current-row` prop |
| Tree data | `row-key` + tree-structured data + `lazy` + `load` |

> [!WARNING]
> When using fixed columns with `show-summary`, the scrollbar may not work correctly (known issue #23142, #20515). Test with your specific data layout.

#### Tag

```html
<el-tag>Default</el-tag>
<el-tag type="success">Success</el-tag>
<el-tag type="warning">Warning</el-tag>
<el-tag type="danger">Danger</el-tag>
<el-tag closable @close="handleClose">Closable</el-tag>
```

#### Badge

```html
<el-badge :value="12">
  <el-button>Comments</el-button>
</el-badge>
<el-badge :value="200" :max="99">
  <el-button>Replies</el-button>
</el-badge>
<el-badge is-dot>
  <el-button>Notifications</el-button>
</el-badge>
```

#### Tree

```html
<el-tree :data="data" :props="{ children: 'children', label: 'label' }"
  show-checkbox node-key="id"
  @check-change="handleCheckChange">
</el-tree>
```

Tree supports lazy loading, drag-and-drop (`draggable`), custom node content via scoped slots, filtering, and accordion mode.

> [!TIP]
> Set `node-key` to a unique identifier in your data for reliable programmatic access to nodes via methods like `getCheckedKeys()`, `setCheckedKeys()`, and `getNode()`.

#### Pagination

```html
<el-pagination
  @size-change="handleSizeChange"
  @current-change="handlePageChange"
  :current-page="currentPage"
  :page-sizes="[10, 20, 50, 100]"
  :page-size="20"
  layout="total, sizes, prev, pager, next, jumper"
  :total="400">
</el-pagination>
```

The `layout` prop controls which elements appear: `sizes`, `prev`, `pager`, `next`, `jumper`, `->` (right-align separator), `total`, `slot`.

#### Progress

```html
<el-progress :percentage="50"></el-progress>
<el-progress :percentage="75" status="success"></el-progress>
<el-progress :percentage="80" :stroke-width="18" text-inside></el-progress>
<el-progress type="circle" :percentage="60"></el-progress>
<el-progress type="dashboard" :percentage="80"></el-progress>
```

#### Descriptions

Display multiple fields in a list layout:

```html
<el-descriptions title="User Info" :column="3" border>
  <el-descriptions-item label="Name">John Doe</el-descriptions-item>
  <el-descriptions-item label="Email">[email removed]</el-descriptions-item>
  <el-descriptions-item label="Role">Admin</el-descriptions-item>
</el-descriptions>
```

#### Skeleton

Loading placeholder while data fetches:

```html
<el-skeleton :loading="loading" animated :rows="5">
  <template slot="template">
    <el-skeleton-item variant="h1" style="width: 50%" />
    <el-skeleton-item variant="text" />
    <el-skeleton-item variant="text" />
    <el-skeleton-item variant="text" style="width: 60%" />
  </template>
  <template>
    <!-- Actual content here -->
  </template>
</el-skeleton>
```

#### Statistic

Display a statistic with optional prefix/suffix:

```html
<el-statistic :value="268500" title="Active Users"></el-statistic>
```

#### Empty and Result

```html
<el-empty description="No data available"></el-empty>

<el-result icon="success" title="Success" sub-title="Your operation completed">
  <template slot="extra">
    <el-button type="primary">Back</el-button>
  </template>
</el-result>
```

#### Avatar

```html
<el-avatar src="https://example.com/avatar.jpg"></el-avatar>
<el-avatar icon="el-icon-user-solid"></el-avatar>
<el-avatar :size="50" shape="square">User</el-avatar>
```

#### Calendar

```html
<el-calendar v-model="date"></el-calendar>
```

### Navigation

#### Menu

```html
<!-- Horizontal -->
<el-menu mode="horizontal" :default-active="activeIndex" @select="handleSelect">
  <el-menu-item index="1">Home</el-menu-item>
  <el-submenu index="2">
    <template slot="title">Products</template>
    <el-menu-item index="2-1">Product A</el-menu-item>
    <el-menu-item index="2-2">Product B</el-menu-item>
  </el-submenu>
  <el-menu-item index="3">About</el-menu-item>
</el-menu>

<!-- Vertical with collapse -->
<el-menu :default-active="activeIndex" :collapse="isCollapsed">
  <el-menu-item index="1"><i class="el-icon-house"></i><span>Home</span></el-menu-item>
  <el-submenu index="2">
    <template slot="title"><i class="el-icon-setting"></i><span>Settings</span></template>
    <el-menu-item index="2-1">General</el-menu-item>
    <el-menu-item index="2-2">Security</el-menu-item>
  </el-submenu>
</el-menu>
```

Enable Vue Router integration by adding the `router` prop — menu items use their `index` as the route path.

#### Tabs

```html
<el-tabs v-model="activeTab" type="card" @tab-click="handleClick">
  <el-tab-pane label="Tab 1" name="first">Content 1</el-tab-pane>
  <el-tab-pane label="Tab 2" name="second">Content 2</el-tab-pane>
  <el-tab-pane label="Tab 3" name="third" lazy>Content 3 (lazy)</el-tab-pane>
</el-tabs>
```

Tab types: default (no type), `card`, `border-card`. Positions: `top` (default), `right`, `bottom`, `left`.

#### Breadcrumb

```html
<el-breadcrumb separator="/">
  <el-breadcrumb-item :to="{ path: '/' }">Home</el-breadcrumb-item>
  <el-breadcrumb-item>Products</el-breadcrumb-item>
  <el-breadcrumb-item>Detail</el-breadcrumb-item>
</el-breadcrumb>
```

#### Steps

```html
<el-steps :active="active" finish-status="success">
  <el-step title="Step 1" description="Description"></el-step>
  <el-step title="Step 2"></el-step>
  <el-step title="Step 3"></el-step>
</el-steps>
```

Supports `direction="vertical"`, `simple` theme, and custom icons.

#### Dropdown

```html
<el-dropdown @command="handleCommand">
  <span class="el-dropdown-link">
    Actions<i class="el-icon-arrow-down el-icon--right"></i>
  </span>
  <el-dropdown-menu slot="dropdown">
    <el-dropdown-item command="edit">Edit</el-dropdown-item>
    <el-dropdown-item command="delete" divided>Delete</el-dropdown-item>
  </el-dropdown-menu>
</el-dropdown>
```

#### PageHeader

```html
<el-page-header @back="goBack" content="Detail Page" title="Back">
</el-page-header>
```

### Feedback Components

#### Dialog

```html
<el-button @click="dialogVisible = true">Open Dialog</el-button>

<el-dialog title="Dialog Title" :visible.sync="dialogVisible" width="50%">
  <p>Dialog content here.</p>
  <span slot="footer">
    <el-button @click="dialogVisible = false">Cancel</el-button>
    <el-button type="primary" @click="dialogVisible = false">Confirm</el-button>
  </span>
</el-dialog>
```

| Prop | Type | Default | Description |
|---|---|---|---|
| `visible` | `boolean` | `false` | Use with `.sync` modifier |
| `title` | `string` | — | Dialog title |
| `width` | `string` | `'50%'` | Dialog width |
| `fullscreen` | `boolean` | `false` | Fullscreen mode |
| `top` | `string` | `'15vh'` | Margin from top |
| `modal` | `boolean` | `true` | Show overlay mask |
| `close-on-click-modal` | `boolean` | `true` | Close on mask click |
| `close-on-press-escape` | `boolean` | `true` | Close on Escape key |
| `destroy-on-close` | `boolean` | `false` | Destroy inner content on close |

#### Drawer

Side panel alternative to Dialog:

```html
<el-drawer title="Settings" :visible.sync="drawerVisible" direction="rtl" size="30%">
  <p>Drawer content</p>
</el-drawer>
```

Direction options: `rtl` (right), `ltr` (left), `ttb` (top), `btt` (bottom).

> [!WARNING]
> When nesting Drawers or combining Drawers with Dialogs, set `append-to-body` on the inner component. Improper nesting without this prop can cause z-index layering issues (see issue #23137).

#### Tooltip

```html
<el-tooltip content="Helpful tip" placement="top">
  <el-button>Hover me</el-button>
</el-tooltip>
```

Placement: `top`, `top-start`, `top-end`, `bottom`, `left`, `right` (and their `-start`/`-end` variants).

#### Popover

```html
<el-popover placement="right" width="200" trigger="click" content="Popover content">
  <el-button slot="reference">Click me</el-button>
</el-popover>
```

Trigger modes: `click`, `focus`, `hover`, `manual`.

#### Popconfirm

```html
<el-popconfirm title="Are you sure?" @confirm="handleConfirm">
  <el-button slot="reference">Delete</el-button>
</el-popconfirm>
```

#### Alert

```html
<el-alert title="Info alert" type="info"></el-alert>
<el-alert title="Success" type="success" description="Operation completed" show-icon closable>
</el-alert>
```

Types: `success`, `warning`, `info`, `error`. Effects: `light` (default), `dark`.

### Service Components

These components are invoked programmatically via `this.$method()` rather than as template tags.

#### Message

Global message displayed at the top of the page:

```javascript
// Simple
this.$message('This is a message');

// Typed
this.$message.success('Success!');
this.$message.warning('Warning!');
this.$message.error('Error!');

// With options
this.$message({
  message: 'Congratulations!',
  type: 'success',
  duration: 5000,
  showClose: true,
  center: true,
  offset: 40
});

// Close all messages
this.$message.closeAll();
```

| Option | Type | Default | Description |
|---|---|---|---|
| `message` | `string/VNode` | — | Message text |
| `type` | `string` | `'info'` | `'success'`, `'warning'`, `'info'`, `'error'` |
| `duration` | `number` | `3000` | Display time in ms (0 = persistent) |
| `showClose` | `boolean` | `false` | Show close button |
| `center` | `boolean` | `false` | Center text |
| `offset` | `number` | `20` | Vertical offset from top in pixels |
| `dangerouslyUseHTMLString` | `boolean` | `false` | Treat message as HTML |
| `onClose` | `function` | `null` | Callback when closed |

#### Notification

Corner notification with title and message:

```javascript
this.$notify({
  title: 'Title',
  message: 'This is a notification',
  type: 'success',
  position: 'top-right',
  duration: 4500
});

// Shorthand
this.$notify.error({ title: 'Error', message: 'Something failed' });

// Close all
this.$notify.closeAll();
```

| Option | Type | Default | Description |
|---|---|---|---|
| `title` | `string` | — | Notification title |
| `message` | `string/VNode` | — | Notification body |
| `type` | `string` | — | `'success'`, `'warning'`, `'info'`, `'error'` |
| `position` | `string` | `'top-right'` | `'top-right'`, `'top-left'`, `'bottom-right'`, `'bottom-left'` |
| `duration` | `number` | `4500` | Display time in ms (0 = persistent) |
| `showClose` | `boolean` | `true` | Show close button |
| `offset` | `number` | `0` | Vertical offset in pixels |
| `onClose` | `function` | `null` | Close callback |
| `onClick` | `function` | `null` | Click callback |

#### MessageBox

Modal dialog for confirmations and prompts:

```javascript
// Alert
this.$alert('This is an alert', 'Title', { confirmButtonText: 'OK' });

// Confirm
this.$confirm('Are you sure?', 'Warning', {
  confirmButtonText: 'Yes',
  cancelButtonText: 'No',
  type: 'warning'
}).then(() => {
  this.$message.success('Confirmed');
}).catch(() => {
  this.$message.info('Cancelled');
});

// Prompt
this.$prompt('Enter your email', 'Prompt', {
  inputPattern: /^[\w-]+(\.[\w-]+)*@[\w-]+(\.[\w-]+)+$/,
  inputErrorMessage: 'Invalid email format'
}).then(({ value }) => {
  this.$message.success('Email: ' + value);
});
```

> [!NOTE]
> MessageBox returns a Promise. Use `.then()` for confirm and `.catch()` for cancel. Both `$alert` and `$confirm` use the same underlying `$msgbox` service.

#### Loading

**Directive usage:**

```html
<div v-loading="isLoading">Content that's loading...</div>

<!-- Fullscreen -->
<div v-loading.fullscreen.lock="isLoading">Full page loading</div>
```

Customize via element attributes:

```html
<div v-loading="true"
  element-loading-text="Loading..."
  element-loading-spinner="el-icon-loading"
  element-loading-background="rgba(0, 0, 0, 0.8)">
</div>
```

**Service usage:**

```javascript
const loading = this.$loading({
  lock: true,
  text: 'Loading...',
  spinner: 'el-icon-loading',
  background: 'rgba(0, 0, 0, 0.7)',
  target: document.querySelector('.target-element')
});

// Close when done
loading.close();
```

### Other Components

#### Carousel

```html
<el-carousel :interval="3000" height="200px">
  <el-carousel-item v-for="item in 4" :key="item">
    <div>Slide {{ item }}</div>
  </el-carousel-item>
</el-carousel>
```

Supports `type="card"` for card-style layout, `direction="vertical"`, and `arrow="always"`.

#### Collapse

```html
<el-collapse v-model="activeNames">
  <el-collapse-item title="Section 1" name="1">Content 1</el-collapse-item>
  <el-collapse-item title="Section 2" name="2">Content 2</el-collapse-item>
</el-collapse>

<!-- Accordion mode -->
<el-collapse accordion v-model="activeName">
  <el-collapse-item title="Panel A" name="a">Only one open at a time</el-collapse-item>
  <el-collapse-item title="Panel B" name="b">Content B</el-collapse-item>
</el-collapse>
```

#### Timeline

```html
<el-timeline>
  <el-timeline-item timestamp="2024-01-15" type="primary">Event 1</el-timeline-item>
  <el-timeline-item timestamp="2024-02-01" type="success">Event 2</el-timeline-item>
</el-timeline>
```

#### Image

```html
<el-image src="/img/photo.jpg" fit="cover" lazy>
  <div slot="placeholder">Loading...</div>
  <div slot="error"><i class="el-icon-picture-outline"></i></div>
</el-image>

<!-- With preview -->
<el-image src="/img/photo.jpg" :preview-src-list="['/img/photo.jpg', '/img/photo2.jpg']">
</el-image>
```

Supports lazy loading (`lazy`), object-fit modes (`fill`, `contain`, `cover`, `none`, `scale-down`), and full-screen image preview.

#### Backtop

```html
<el-backtop :visibility-height="200" :right="40" :bottom="40"></el-backtop>
```

#### InfiniteScroll

```html
<ul v-infinite-scroll="loadMore" infinite-scroll-disabled="disabled"
  infinite-scroll-distance="10">
  <li v-for="item in list" :key="item.id">{{ item.name }}</li>
</ul>
```

| Attribute | Type | Default | Description |
|---|---|---|---|
| `infinite-scroll-disabled` | `boolean` | `false` | Disable the scroll trigger |
| `infinite-scroll-delay` | `number` | `200` | Throttle delay in ms |
| `infinite-scroll-distance` | `number` | `0` | Distance threshold in px |
| `infinite-scroll-immediate` | `boolean` | `true` | Execute immediately if content doesn't fill the container |

#### Scrollbar

Custom scrollbar component (wraps content):

```html
<el-scrollbar style="height: 300px;">
  <div>Long content...</div>
</el-scrollbar>
```

#### Card and Divider

```html
<el-card shadow="hover">
  <div slot="header">Card Title</div>
  <p>Card content</p>
</el-card>

<el-divider></el-divider>
<el-divider content-position="center">Section Title</el-divider>
<el-divider direction="vertical"></el-divider>
```

## 5. Common Tasks

### Build a complete CRUD form

**Goal:** Create a form with validation, submit to server, show success/error feedback.

**Steps:**

1. Wrap fields in `<el-form>` with `:model` and `:rules`
2. Each field in `<el-form-item>` with `prop` matching model key
3. Validate on submit with `this.$refs.form.validate()`
4. Show feedback with `this.$message.success()` or `this.$message.error()`
5. Reset with `this.$refs.form.resetFields()`

**Verify:** All required fields show validation errors when empty. Success message appears after valid submit.

### Build a data table with sorting, filtering, and pagination

**Goal:** Display server data with column sort, filter dropdowns, and page navigation.

**Steps:**

1. Use `<el-table>` with `:data` bound to current page data
2. Add `sortable` to columns that should be sortable
3. Add `filters` and `:filter-method` to filterable columns
4. Place `<el-pagination>` below the table
5. Handle `@current-change` and `@size-change` events to fetch new data

### Create a multi-level navigation sidebar

**Goal:** Vertical collapsible menu with router integration.

**Steps:**

1. Use `<el-menu>` with `router` prop and `:collapse` bound to a toggle
2. Add `<el-menu-item>` for leaf pages (set `index` to route path)
3. Wrap grouped items in `<el-submenu>`
4. Toggle collapse state with a button

### Implement file upload with preview

**Goal:** Upload images with thumbnail preview and drag support.

**Steps:**

1. Use `<el-upload>` with `list-type="picture-card"` and `drag`
2. Set `:action` to your upload endpoint
3. Use `:before-upload` to validate file type and size
4. Handle `:on-success` to store server response
5. Use `:on-exceed` to warn when file limit is reached

### Build a date-ranged report filter

**Goal:** Let users pick a date range and filter a table.

**Steps:**

1. Use `<el-date-picker type="daterange">` with `v-model`
2. Watch the model value and re-fetch data on change
3. Use `value-format="yyyy-MM-dd"` for consistent string output

### Customize the theme

**Goal:** Change primary color and component sizes globally.

**Steps:**

1. Install `element-theme`: `npm install element-theme element-theme-chalk -g`
2. Initialize: `et -i` (generates `element-variables.scss`)
3. Edit `$--color-primary` and other variables
4. Build: `et`
5. Import the generated theme instead of the default CSS

### Add internationalization

**Goal:** Support multiple languages in your app.

**Steps:**

1. Import desired locale files from `element-ui/lib/locale/lang/`
2. Use `locale.use(lang)` or integrate with vue-i18n
3. Switch languages at runtime by calling `locale.use()` with a different pack

### Create a dynamic form

**Goal:** Add/remove form items dynamically with validation.

**Steps:**

1. Use a reactive array in your model (e.g., `form.items: []`)
2. Loop with `v-for`, using index in the `prop`: `prop="items." + index + ".value"`
3. Set `rules` on each dynamic `<el-form-item>`
4. Add/remove items by pushing to or splicing the array

## 6. Troubleshooting

### Error: `Can not find table layout.`

**Cause:** `<el-table-column>` used outside of `<el-table>`, or Table component not properly initialized.

**Solution:**
1. Ensure every `<el-table-column>` is a direct child of `<el-table>`
2. If columns are conditional, use `v-if` on the column, not on a wrapper div

**Prevention:** Always structure Table markup with columns as direct children.

### Error: `for nested data item, row-key is required.`

**Cause:** Tree-structured table data provided without a `row-key` prop.

**Solution:**
```html
<el-table :data="treeData" row-key="id">
```

### Error: `please transfer a valid prop path to form item!`

**Cause:** The `prop` attribute on `<el-form-item>` doesn't match any key in the form model, or the model is `undefined`.

**Solution:**
1. Verify `:model` is set on `<el-form>`
2. Verify `prop` value matches a key path in the model object
3. For nested paths, use `:` separator: `prop="address:city"`

### Error: `Invalid Date in fecha.format`

**Cause:** DatePicker received an unparseable date value.

**Solution:**
1. Ensure `v-model` is bound to a `Date` object, valid date string, or `null`
2. If using `value-format`, ensure the format string matches your data

### Error: `className should not contain space.`

**Cause:** Passing a class string with spaces to a utility function that expects a single class name.

**Solution:** When programmatically adding classes, pass one class at a time or use an array.

### Table header misalignment with fixed columns

**Cause:** Known rendering issue in Chrome when tables have fixed columns and the content doesn't fill the viewport (issues #21619, #23134).

**Solution:**
1. Call `this.$refs.table.doLayout()` after data changes or tab switches
2. Use `this.$nextTick()` to ensure the DOM has updated before calling `doLayout()`

### Table memory leak when navigating away

**Cause:** Element Table can hold DOM references after component destruction (issue #22980).

**Solution:**
1. Ensure tables are properly destroyed when leaving routes
2. Use `v-if` rather than `v-show` for conditional tables
3. Clear table data before navigation: `this.tableData = []`

### el-select soft keyboard not appearing on Android

**Cause:** Known issue with filterable/remote selects on Android devices where the native keyboard doesn't appear (issue #12563).

**Solution:**
1. Use `readonly` attribute on the inner input and trigger a custom input
2. Or use a native `<select>` on mobile as a fallback

### Cascader placeholder overlap in multi+filterable mode

**Cause:** When both `multiple` and `filterable` are enabled, the placeholder text can overlap with selected tags (issue #23129).

**Solution:** Apply custom CSS to hide the placeholder when tags are present:

```css
.el-cascader .el-input .el-input__inner::placeholder {
  opacity: 0;
}
```

> [!CAUTION]
> Lodash v4.17.17 (bundled as a devDependency) has known security vulnerabilities. If your project uses Element UI's build system, consider upgrading lodash to 4.17.21 or later (issues #23127, #23132).

## 7. FAQ

**Q: Does Element UI support Vue 3?**

No. Element UI (this package) supports Vue 2.x only. For Vue 3, use [Element Plus](https://element-plus.org/), the official successor.

**Q: How do I import only specific components to reduce bundle size?**

Use partial imports with `babel-plugin-component`. See the Installation section for setup details. Each component can be imported individually: `import { Button, Table } from 'element-ui'`.

**Q: How do I change the default language from Chinese?**

Import a language pack and set it before creating your Vue instance. See the Configuration section for examples with both direct locale setting and vue-i18n integration.

**Q: Can I use Element UI with Nuxt.js?**

Yes. Create a plugin file that imports Element UI, and register it in `nuxt.config.js`. Use `ssr: false` for the plugin since Element UI accesses browser APIs.

**Q: How do I customize the theme colors?**

Three approaches: (1) Override SCSS variables and rebuild with `element-theme` CLI, (2) Use the online theme editor at element.eleme.io, or (3) Override CSS custom properties at runtime. See Common Tasks for details.

**Q: Why do my table columns misalign after a tab switch or data change?**

Call `this.$refs.table.doLayout()` inside `this.$nextTick()` after the data changes or the table becomes visible again. This forces the table to recalculate column widths.

**Q: How do I set a global component size?**

Pass `size` in the options when calling `Vue.use(ElementUI, { size: 'small' })`. Valid sizes: `'medium'`, `'small'`, `'mini'`.

**Q: How do I access the Message/Notification service without `this`?**

Import them directly:

```javascript
import { Message, Notification } from 'element-ui';
Message.success('Done');
Notification({ title: 'Info', message: 'Hello' });
```

**Q: How do I validate a form programmatically without user interaction?**

Call `this.$refs.formRef.validate()` which returns a Promise (or accepts a callback). You can also validate individual fields with `this.$refs.formRef.validateField('fieldName')`.

**Q: Can I use Element UI components individually without `Vue.use()`?**

Yes. Import each component and register it locally:

```javascript
import { Button } from 'element-ui';
export default {
  components: { 'el-button': Button }
};
```

---

_Generated June 2026 from ElemeFE/element at c345bb4. AI-generated documentation. The library is checked by sampling (three repositories so far); this guide was not individually reviewed. Verify against the project before relying on it. Unofficial; not affiliated with or reviewed by the project._
