diff --git a/src-next/overlay/README.md b/src-next/overlay/README.md
new file mode 100644
index 000000000..31fba67b1
--- /dev/null
+++ b/src-next/overlay/README.md
@@ -0,0 +1,79 @@
+# Overlay
+
+### Install
+
+```js
+import Vue from 'vue';
+import { Overlay } from 'vant';
+
+Vue.use(Overlay);
+```
+
+## Usage
+
+### Basic Usage
+
+```html
+
+
+```
+
+```js
+export default {
+ data() {
+ return {
+ show: false
+ }
+ }
+},
+```
+
+### Embedded Content
+
+```html
+
+
+
+
+
+```
+
+## API
+
+### Props
+
+| Attribute | Description | Type | Default |
+| --- | --- | --- | --- |
+| show | Whether to show overlay | _boolean_ | `false` |
+| z-index | z-index | _number \| string_ | `1` |
+| duration | Animation duration | _number \| string_ | `0.3` |
+| class-name | ClassName | _string_ | - |
+| custom-class `v2.2.5` | Custom style | _object_ | - |
+| lock-scroll `v2.6.2` | Whether to lock background scroll | _boolean_ | `true` |
+
+### Events
+
+| Event | Description | Arguments |
+| ----- | ---------------------- | -------------- |
+| click | Triggered when clicked | _event: Event_ |
+
+### Slots
+
+| Name | Description |
+| ---------------- | ------------ |
+| default `v2.2.5` | Default slot |
diff --git a/src-next/overlay/README.zh-CN.md b/src-next/overlay/README.zh-CN.md
new file mode 100644
index 000000000..9d70faa1b
--- /dev/null
+++ b/src-next/overlay/README.zh-CN.md
@@ -0,0 +1,85 @@
+# Overlay 遮罩层
+
+### 介绍
+
+创建一个遮罩层,用于强调特定的页面元素,并阻止用户进行其他操作
+
+### 引入
+
+```js
+import Vue from 'vue';
+import { Overlay } from 'vant';
+
+Vue.use(Overlay);
+```
+
+## 代码演示
+
+### 基础用法
+
+```html
+
+
+```
+
+```js
+export default {
+ data() {
+ return {
+ show: false
+ }
+ }
+},
+```
+
+### 嵌入内容
+
+通过默认插槽可以在遮罩层上嵌入任意内容
+
+```html
+
+
+
+
+
+```
+
+## API
+
+### Props
+
+| 参数 | 说明 | 类型 | 默认值 |
+| --- | --- | --- | --- |
+| show | 是否展示遮罩层 | _boolean_ | `false` |
+| z-index | z-index 层级 | _number \| string_ | `1` |
+| duration | 动画时长,单位秒 | _number \| string_ | `0.3` |
+| class-name | 自定义类名 | _string_ | - |
+| custom-style `v2.2.5` | 自定义样式 | _object_ | - |
+| lock-scroll `v2.6.2` | 是否锁定背景滚动,锁定时蒙层里的内容也将无法滚动 | _boolean_ | `true` |
+
+### Events
+
+| 事件名 | 说明 | 回调参数 |
+| ------ | ---------- | -------------- |
+| click | 点击时触发 | _event: Event_ |
+
+### Slots
+
+| 名称 | 说明 |
+| ---------------- | ---------------------------------- |
+| default `v2.0.5` | 默认插槽,用于在遮罩层上方嵌入内容 |
diff --git a/src-next/overlay/demo/index.vue b/src-next/overlay/demo/index.vue
new file mode 100644
index 000000000..a919f4f8c
--- /dev/null
+++ b/src-next/overlay/demo/index.vue
@@ -0,0 +1,72 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src-next/overlay/index.js b/src-next/overlay/index.js
new file mode 100644
index 000000000..87da81781
--- /dev/null
+++ b/src-next/overlay/index.js
@@ -0,0 +1,49 @@
+import { Transition } from 'vue';
+import { createNamespace, isDef, noop } from '../utils';
+import { preventDefault } from '../utils/dom/event';
+
+const [createComponent, bem] = createNamespace('overlay');
+
+function preventTouchMove(event) {
+ preventDefault(event, true);
+}
+
+export default createComponent({
+ props: {
+ show: Boolean,
+ zIndex: [Number, String],
+ duration: [Number, String],
+ className: null,
+ customStyle: Object,
+ lockScroll: {
+ type: Boolean,
+ default: true,
+ },
+ },
+
+ setup(props, { slots }) {
+ return function () {
+ const style = {
+ zIndex: props.zIndex,
+ ...props.customStyle,
+ };
+
+ if (isDef(props.duration)) {
+ style.animationDuration = `${props.duration}s`;
+ }
+
+ return (
+
+
+ {slots.default?.()}
+
+
+ );
+ };
+ },
+});
diff --git a/src-next/overlay/index.less b/src-next/overlay/index.less
new file mode 100644
index 000000000..3b547cff9
--- /dev/null
+++ b/src-next/overlay/index.less
@@ -0,0 +1,11 @@
+@import '../style/var';
+
+.van-overlay {
+ position: fixed;
+ top: 0;
+ left: 0;
+ z-index: @overlay-z-index;
+ width: 100%;
+ height: 100%;
+ background-color: @overlay-background-color;
+}
diff --git a/src-next/overlay/test/demo.spec.js b/src-next/overlay/test/demo.spec.js
new file mode 100644
index 000000000..5c70922b5
--- /dev/null
+++ b/src-next/overlay/test/demo.spec.js
@@ -0,0 +1,4 @@
+import Demo from '../demo';
+import { snapshotDemo } from '../../../test/demo';
+
+snapshotDemo(Demo);
diff --git a/src-next/overlay/test/index.spec.js b/src-next/overlay/test/index.spec.js
new file mode 100644
index 000000000..093aa569c
--- /dev/null
+++ b/src-next/overlay/test/index.spec.js
@@ -0,0 +1,98 @@
+import { mount } from '../../../test';
+import Overlay from '..';
+
+test('z-index prop', () => {
+ const wrapper = mount(Overlay, {
+ propsData: {
+ show: true,
+ zIndex: 99,
+ },
+ });
+
+ expect(wrapper).toMatchSnapshot();
+});
+
+test('class-name prop', () => {
+ const wrapper = mount(Overlay, {
+ propsData: {
+ show: true,
+ className: 'my-overlay',
+ },
+ });
+
+ expect(wrapper).toMatchSnapshot();
+});
+
+test('custom style prop', () => {
+ const wrapper = mount(Overlay, {
+ propsData: {
+ show: true,
+ customStyle: {
+ backgroundColor: 'red',
+ },
+ },
+ });
+
+ expect(wrapper).toMatchSnapshot();
+});
+
+test('duration prop', () => {
+ const wrapper = mount(Overlay, {
+ propsData: {
+ show: true,
+ duration: 1,
+ },
+ });
+
+ expect(wrapper).toMatchSnapshot();
+});
+
+test('click event', () => {
+ const onClick = jest.fn();
+ const wrapper = mount(Overlay, {
+ context: {
+ on: {
+ click: onClick,
+ },
+ },
+ });
+
+ wrapper.trigger('click');
+ expect(onClick).toHaveBeenCalledTimes(1);
+});
+
+test('default slot', () => {
+ const wrapper = mount(Overlay, {
+ scopedSlots: {
+ default: () => 'Custom Default',
+ },
+ });
+
+ expect(wrapper).toMatchSnapshot();
+});
+
+test('lock-scroll prop', () => {
+ const onTouchMove = jest.fn();
+ const wrapper = mount({
+ template: `
+
+
+
+ `,
+ data() {
+ return {
+ lockScroll: true,
+ };
+ },
+ methods: {
+ onTouchMove,
+ },
+ });
+
+ wrapper.find('.van-overlay').trigger('touchmove');
+ expect(onTouchMove).toHaveBeenCalledTimes(0);
+
+ wrapper.setData({ lockScroll: false });
+ wrapper.find('.van-overlay').trigger('touchmove');
+ expect(onTouchMove).toHaveBeenCalledTimes(1);
+});