diff --git a/packages/vant/src/skeleton-avatar/README.md b/packages/vant/src/skeleton-avatar/README.md
new file mode 100644
index 000000000..3d545d84f
--- /dev/null
+++ b/packages/vant/src/skeleton-avatar/README.md
@@ -0,0 +1,3 @@
+# SkeletonAvatar
+
+Please refer to [Skeleton docs](/skeleton#skeletonavatar-props)
diff --git a/packages/vant/src/skeleton/SkeletonAvatar.tsx b/packages/vant/src/skeleton-avatar/SkeletonAvatar.tsx
similarity index 91%
rename from packages/vant/src/skeleton/SkeletonAvatar.tsx
rename to packages/vant/src/skeleton-avatar/SkeletonAvatar.tsx
index 3f1f0a23f..708f6a1ed 100644
--- a/packages/vant/src/skeleton/SkeletonAvatar.tsx
+++ b/packages/vant/src/skeleton-avatar/SkeletonAvatar.tsx
@@ -8,8 +8,7 @@ import {
createNamespace,
} from '../utils';
-// Types
-import type { SkeletonAvatarShape } from './types';
+export type SkeletonAvatarShape = 'square' | 'round';
const [name, bem] = createNamespace('skeleton-avatar');
diff --git a/packages/vant/src/skeleton-avatar/index.less b/packages/vant/src/skeleton-avatar/index.less
new file mode 100644
index 000000000..3894179ff
--- /dev/null
+++ b/packages/vant/src/skeleton-avatar/index.less
@@ -0,0 +1,22 @@
+:root {
+ --van-skeleton-avatar-size: 32px;
+ --van-skeleton-avatar-background: var(--van-active-color);
+}
+
+.van-skeleton {
+ &-avatar {
+ flex-shrink: 0;
+ width: var(--van-skeleton-avatar-size);
+ height: var(--van-skeleton-avatar-size);
+ margin-right: var(--van-padding-md);
+ background: var(--van-skeleton-avatar-background);
+
+ &--round {
+ border-radius: var(--van-radius-max);
+ }
+ }
+
+ &-avatar + &__content {
+ padding-top: var(--van-padding-xs);
+ }
+}
diff --git a/packages/vant/src/skeleton-avatar/index.ts b/packages/vant/src/skeleton-avatar/index.ts
new file mode 100644
index 000000000..97882b89e
--- /dev/null
+++ b/packages/vant/src/skeleton-avatar/index.ts
@@ -0,0 +1,17 @@
+import { withInstall } from '../utils';
+import _SkeletonAvatar from './SkeletonAvatar';
+
+export const SkeletonAvatar = withInstall(_SkeletonAvatar);
+export default SkeletonAvatar;
+
+export { skeletonAvatarProps } from './SkeletonAvatar';
+export type {
+ SkeletonAvatarProps,
+ SkeletonAvatarShape,
+} from './SkeletonAvatar';
+
+declare module 'vue' {
+ export interface GlobalComponents {
+ VanSkeletonAvatar: typeof SkeletonAvatar;
+ }
+}
diff --git a/packages/vant/src/skeleton-avatar/test/__snapshots__/index.spec.tsx.snap b/packages/vant/src/skeleton-avatar/test/__snapshots__/index.spec.tsx.snap
new file mode 100644
index 000000000..102432021
--- /dev/null
+++ b/packages/vant/src/skeleton-avatar/test/__snapshots__/index.spec.tsx.snap
@@ -0,0 +1,13 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should skeleton avatar render correctly when change props 1`] = `
+
+
+`;
+
+exports[`should skeleton avatar render correctly without props 1`] = `
+
+
+`;
diff --git a/packages/vant/src/skeleton-avatar/test/index.spec.tsx b/packages/vant/src/skeleton-avatar/test/index.spec.tsx
new file mode 100644
index 000000000..457cd8b68
--- /dev/null
+++ b/packages/vant/src/skeleton-avatar/test/index.spec.tsx
@@ -0,0 +1,19 @@
+import { mount } from '../../../test';
+import { SkeletonAvatar } from '..';
+
+test('should skeleton avatar render correctly without props', () => {
+ const wrapper = mount(SkeletonAvatar);
+
+ expect(wrapper.html()).toMatchSnapshot();
+});
+
+test('should skeleton avatar render correctly when change props', () => {
+ const wrapper = mount(SkeletonAvatar, {
+ props: {
+ avatarSize: 50,
+ avatarShape: 'square',
+ },
+ });
+
+ expect(wrapper.html()).toMatchSnapshot();
+});
diff --git a/packages/vant/src/skeleton-image/README.md b/packages/vant/src/skeleton-image/README.md
new file mode 100644
index 000000000..2b76a6fad
--- /dev/null
+++ b/packages/vant/src/skeleton-image/README.md
@@ -0,0 +1,3 @@
+# SkeletonImage
+
+Please refer to [Skeleton docs](/skeleton#skeletonimage-props)
diff --git a/packages/vant/src/skeleton/SkeletonImage.tsx b/packages/vant/src/skeleton-image/SkeletonImage.tsx
similarity index 92%
rename from packages/vant/src/skeleton/SkeletonImage.tsx
rename to packages/vant/src/skeleton-image/SkeletonImage.tsx
index bab553dae..237159dfb 100644
--- a/packages/vant/src/skeleton/SkeletonImage.tsx
+++ b/packages/vant/src/skeleton-image/SkeletonImage.tsx
@@ -7,13 +7,12 @@ import {
createNamespace,
} from '../utils';
-// Types
-import type { SkeletonImageShape } from './types';
-
import { Icon } from '../icon';
const [name, bem] = createNamespace('skeleton-image');
+export type SkeletonImageShape = 'square' | 'round';
+
export const skeletonImageProps = {
imageSize: numericProp,
imageShape: makeStringProp('square'),
diff --git a/packages/vant/src/skeleton-image/index.less b/packages/vant/src/skeleton-image/index.less
new file mode 100644
index 000000000..eaf975a46
--- /dev/null
+++ b/packages/vant/src/skeleton-image/index.less
@@ -0,0 +1,26 @@
+:root {
+ --van-skeleton-image-size: 96px;
+ --van-skeleton-image-radius: 24px;
+}
+
+.van-skeleton {
+ &-image {
+ display: flex;
+ width: var(--van-skeleton-image-size);
+ height: var(--van-skeleton-image-size);
+ align-items: center;
+ justify-content: center;
+ background: var(--van-active-color);
+
+ &--round {
+ border-radius: var(--van-skeleton-image-radius);
+ }
+
+ &__icon {
+ width: calc(var(--van-skeleton-image-size) / 2);
+ height: calc(var(--van-skeleton-image-size) / 2);
+ font-size: calc(var(--van-skeleton-image-size) / 2);
+ color: var(--van-gray-5);
+ }
+ }
+}
diff --git a/packages/vant/src/skeleton-image/index.ts b/packages/vant/src/skeleton-image/index.ts
new file mode 100644
index 000000000..9338949f1
--- /dev/null
+++ b/packages/vant/src/skeleton-image/index.ts
@@ -0,0 +1,14 @@
+import _SkeletonImage from './SkeletonImage';
+import { withInstall } from '../utils';
+
+export const SkeletonImage = withInstall(_SkeletonImage);
+export default SkeletonImage;
+
+export { skeletonImageProps } from './SkeletonImage';
+export type { SkeletonImageProps, SkeletonImageShape } from './SkeletonImage';
+
+declare module 'vue' {
+ export interface GlobalComponents {
+ VanSkeletonImage: typeof SkeletonImage;
+ }
+}
diff --git a/packages/vant/src/skeleton-image/test/__snapshots__/index.spec.tsx.snap b/packages/vant/src/skeleton-image/test/__snapshots__/index.spec.tsx.snap
new file mode 100644
index 000000000..9d755403c
--- /dev/null
+++ b/packages/vant/src/skeleton-image/test/__snapshots__/index.spec.tsx.snap
@@ -0,0 +1,8 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should skeleton image render correctly 1`] = `
+
+
+
+
+`;
diff --git a/packages/vant/src/skeleton-image/test/index.spec.tsx b/packages/vant/src/skeleton-image/test/index.spec.tsx
new file mode 100644
index 000000000..d230532f6
--- /dev/null
+++ b/packages/vant/src/skeleton-image/test/index.spec.tsx
@@ -0,0 +1,30 @@
+import { mount } from '../../../test';
+import { SkeletonImage } from '..';
+
+test('should skeleton image render correctly', () => {
+ const wrapper = mount(SkeletonImage);
+
+ expect(wrapper.html()).toMatchSnapshot();
+});
+
+test('should skeleton image works with imageSize prop', () => {
+ const wrapper = mount(SkeletonImage, {
+ props: {
+ imageSize: '20rem',
+ },
+ });
+ const dom = wrapper.find('.van-skeleton-image');
+
+ expect(dom.style.width).toBe('20rem');
+ expect(dom.style.height).toBe('20rem');
+});
+
+test('should skeleton image works with imageShape prop', () => {
+ const wrapper = mount(SkeletonImage, {
+ props: {
+ imageShape: 'round',
+ },
+ });
+
+ expect(wrapper.find('.van-skeleton-image--round')).toBeTruthy();
+});
diff --git a/packages/vant/src/skeleton-paragraph/README.md b/packages/vant/src/skeleton-paragraph/README.md
new file mode 100644
index 000000000..644a13811
--- /dev/null
+++ b/packages/vant/src/skeleton-paragraph/README.md
@@ -0,0 +1,3 @@
+# SkeletonParagraph
+
+Please refer to [Skeleton docs](/skeleton#skeletonparagraph-props)
diff --git a/packages/vant/src/skeleton/SkeletonParagraph.tsx b/packages/vant/src/skeleton-paragraph/SkeletonParagraph.tsx
similarity index 100%
rename from packages/vant/src/skeleton/SkeletonParagraph.tsx
rename to packages/vant/src/skeleton-paragraph/SkeletonParagraph.tsx
diff --git a/packages/vant/src/skeleton-paragraph/index.less b/packages/vant/src/skeleton-paragraph/index.less
new file mode 100644
index 000000000..b5fd7b5e7
--- /dev/null
+++ b/packages/vant/src/skeleton-paragraph/index.less
@@ -0,0 +1,20 @@
+:root {
+ --van-skeleton-paragraph-height: 16px;
+ --van-skeleton-paragraph-background: var(--van-active-color);
+ --van-skeleton-paragraph-margin-top: var(--van-padding-sm);
+}
+
+.van-skeleton {
+ &-paragraph {
+ height: var(--van-skeleton-paragraph-height);
+ background: var(--van-skeleton-paragraph-background);
+
+ &--round {
+ border-radius: var(--van-radius-max);
+ }
+
+ &:not(:first-child) {
+ margin-top: var(--van-skeleton-paragraph-margin-top);
+ }
+ }
+}
diff --git a/packages/vant/src/skeleton-paragraph/index.ts b/packages/vant/src/skeleton-paragraph/index.ts
new file mode 100644
index 000000000..69dad82ca
--- /dev/null
+++ b/packages/vant/src/skeleton-paragraph/index.ts
@@ -0,0 +1,14 @@
+import _SkeletonParagraph from './SkeletonParagraph';
+import { withInstall } from '../utils';
+
+export const SkeletonParagraph = withInstall(_SkeletonParagraph);
+export default SkeletonParagraph;
+
+export { skeletonParagraphProps, DEFAULT_ROW_WIDTH } from './SkeletonParagraph';
+export type { SkeletonParagraphProps } from './SkeletonParagraph';
+
+declare module 'vue' {
+ export interface GlobalComponents {
+ VanSkeletonParagraph: typeof SkeletonParagraph;
+ }
+}
diff --git a/packages/vant/src/skeleton-paragraph/test/__snapshots__/index.spec.tsx.snap b/packages/vant/src/skeleton-paragraph/test/__snapshots__/index.spec.tsx.snap
new file mode 100644
index 000000000..45037e123
--- /dev/null
+++ b/packages/vant/src/skeleton-paragraph/test/__snapshots__/index.spec.tsx.snap
@@ -0,0 +1,15 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should skeleton paragraph works 1`] = `
+
+
+`;
+
+exports[`should skeleton paragraph works with props 1`] = `
+
+
+`;
diff --git a/packages/vant/src/skeleton-paragraph/test/index.spec.tsx b/packages/vant/src/skeleton-paragraph/test/index.spec.tsx
new file mode 100644
index 000000000..c261ec0a4
--- /dev/null
+++ b/packages/vant/src/skeleton-paragraph/test/index.spec.tsx
@@ -0,0 +1,19 @@
+import { mount } from '../../../test';
+import { SkeletonParagraph } from '..';
+
+test('should skeleton paragraph works', () => {
+ const wrapper = mount(SkeletonParagraph);
+
+ expect(wrapper.html()).toMatchSnapshot();
+});
+
+test('should skeleton paragraph works with props', () => {
+ const wrapper = mount(SkeletonParagraph, {
+ props: {
+ round: true,
+ rowWidth: '200rem',
+ },
+ });
+
+ expect(wrapper.html()).toMatchSnapshot();
+});
diff --git a/packages/vant/src/skeleton-title/README.md b/packages/vant/src/skeleton-title/README.md
new file mode 100644
index 000000000..42388d826
--- /dev/null
+++ b/packages/vant/src/skeleton-title/README.md
@@ -0,0 +1,3 @@
+# SkeletonParagraph
+
+Please refer to [Skeleton docs](/skeleton#skeletontitle-props)
diff --git a/packages/vant/src/skeleton/SkeletonTitle.tsx b/packages/vant/src/skeleton-title/SkeletonTitle.tsx
similarity index 100%
rename from packages/vant/src/skeleton/SkeletonTitle.tsx
rename to packages/vant/src/skeleton-title/SkeletonTitle.tsx
diff --git a/packages/vant/src/skeleton-title/index.less b/packages/vant/src/skeleton-title/index.less
new file mode 100644
index 000000000..ddc8123c7
--- /dev/null
+++ b/packages/vant/src/skeleton-title/index.less
@@ -0,0 +1,23 @@
+:root {
+ --van-skeleton-title-width: 40%;
+}
+
+.van-skeleton {
+ &-title {
+ height: var(--van-skeleton-paragraph-height);
+ background: var(--van-skeleton-paragraph-background);
+
+ &--round {
+ border-radius: var(--van-radius-max);
+ }
+ }
+
+ &-title {
+ width: var(--van-skeleton-title-width);
+ margin: 0;
+ }
+
+ &-title + &-paragraph {
+ margin-top: 20px;
+ }
+}
diff --git a/packages/vant/src/skeleton-title/index.ts b/packages/vant/src/skeleton-title/index.ts
new file mode 100644
index 000000000..c30a26b49
--- /dev/null
+++ b/packages/vant/src/skeleton-title/index.ts
@@ -0,0 +1,14 @@
+import _SkeletonTitle from './SkeletonTitle';
+import { withInstall } from '../utils';
+
+export const SkeletonTitle = withInstall(_SkeletonTitle);
+export default SkeletonTitle;
+
+export { skeletonTitleProps } from './SkeletonTitle';
+export type { SkeletonTitleProps } from './SkeletonTitle';
+
+declare module 'vue' {
+ export interface GlobalComponents {
+ VanSkeletonTitle: typeof SkeletonTitle;
+ }
+}
diff --git a/packages/vant/src/skeleton-title/test/__snapshots__/index.spec.tsx.snap b/packages/vant/src/skeleton-title/test/__snapshots__/index.spec.tsx.snap
new file mode 100644
index 000000000..a000558b6
--- /dev/null
+++ b/packages/vant/src/skeleton-title/test/__snapshots__/index.spec.tsx.snap
@@ -0,0 +1,13 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should skeleton title works 1`] = `
+
+
+`;
+
+exports[`should skeleton title works with props 1`] = `
+
+
+`;
diff --git a/packages/vant/src/skeleton-title/test/index.spec.tsx b/packages/vant/src/skeleton-title/test/index.spec.tsx
new file mode 100644
index 000000000..a0bbe85b3
--- /dev/null
+++ b/packages/vant/src/skeleton-title/test/index.spec.tsx
@@ -0,0 +1,19 @@
+import { mount } from '../../../test';
+import { SkeletonTitle } from '..';
+
+test('should skeleton title works', () => {
+ const wrapper = mount(SkeletonTitle);
+
+ expect(wrapper.html()).toMatchSnapshot();
+});
+
+test('should skeleton title works with props', () => {
+ const wrapper = mount(SkeletonTitle, {
+ props: {
+ round: true,
+ titleWidth: '200rem',
+ },
+ });
+
+ expect(wrapper.html()).toMatchSnapshot();
+});
diff --git a/packages/vant/src/skeleton/Skeleton.tsx b/packages/vant/src/skeleton/Skeleton.tsx
index 39d1f1f53..b9b2b9ad9 100644
--- a/packages/vant/src/skeleton/Skeleton.tsx
+++ b/packages/vant/src/skeleton/Skeleton.tsx
@@ -12,12 +12,12 @@ import {
} from '../utils';
// Components
-import SkeletonTitle from './SkeletonTitle';
-import SkeletonAvatar from './SkeletonAvatar';
-import SkeletonParagraph, { DEFAULT_ROW_WIDTH } from './SkeletonParagraph';
+import SkeletonTitle from '../skeleton-title';
+import SkeletonAvatar from '../skeleton-avatar';
+import SkeletonParagraph, { DEFAULT_ROW_WIDTH } from '../skeleton-paragraph';
// Types
-import type { SkeletonAvatarShape } from './types';
+import type { SkeletonAvatarShape } from '../skeleton-avatar';
const [name, bem] = createNamespace('skeleton');
const DEFAULT_LAST_ROW_WIDTH = '60%';
diff --git a/packages/vant/src/skeleton/demo/index.vue b/packages/vant/src/skeleton/demo/index.vue
index 3e7f2eba3..5860b2046 100644
--- a/packages/vant/src/skeleton/demo/index.vue
+++ b/packages/vant/src/skeleton/demo/index.vue
@@ -1,8 +1,7 @@