Feedback · Skeleton

Skeleton

A placeholder preview of content before the data loaded.

Overview

  • Avatar: Avatar placeholder, by default uses Avatar medium sizing: width: 48px, height: 48px. Supports the size and shape properties; see the API reference for their values.
  • Image: Image placeholder, default size: width: 100%, height: 100%.
  • Title: Title placeholder, default size: width: 100%, height: 24px.
  • Paragraph: Content part placeholder, default size: width: 100%, height: 16px, margin-bottom: 10px.
  • Button: Button placeholder, default size: width: 115px, height: 32px.

Note: Default styles could by overwritten through class or style.

Demos

How to import

<script setup lang="ts">
    import { Skeleton } from '@fibrous/semi-ui-vue';
</script>

Basic Usage

显示加载内容




Code
<script setup lang="ts">
    import { ref } from 'vue';
    import { Avatar, Button, Skeleton, Switch } from '@fibrous/semi-ui-vue';

    const loading = ref(true);
</script>

<template>
    <span :style="{ display: 'flex', alignItems: 'center' }">
        <Switch aria-labelledby="skeleton-basic-toggle" @change="loading = !loading" />
        <span id="skeleton-basic-toggle" :style="{ marginLeft: '10px' }">显示加载内容</span>
    </span>
    <br />
    <Skeleton :loading="loading">
        <template #placeholder>
            <Skeleton.Avatar />
        </template>
        <Avatar color="blue" :style="{ marginBottom: '10px' }">U</Avatar>
    </Skeleton>
    <br />
    <Skeleton :loading="loading" :style="{ width: '200px', height: '150px' }">
        <template #placeholder>
            <Skeleton.Image />
        </template>
        <img alt="avatar" height="150" src="/assets/placeholder/avatar-a.svg" />
    </Skeleton>
    <br />
    <Skeleton :loading="loading" :style="{ width: '80px' }">
        <template #placeholder>
            <Skeleton.Title :style="{ marginBottom: '10px' }" />
        </template>
        <h4 :style="{ marginBottom: 0 }">Semi UI</h4>
    </Skeleton>
    <Skeleton :loading="loading" :style="{ width: '240px' }">
        <template #placeholder>
            <Skeleton.Paragraph :rows="2" />
        </template>
        <p :style="{ width: '240px' }">精心打磨每一个组件的用户体验,从用户的角度考虑每个组件的使用场景。</p>
    </Skeleton>
    <br />
    <Skeleton :loading="loading">
        <template #placeholder>
            <Skeleton.Button />
        </template>
        <Button>Button</Button>
    </Skeleton>
</template>

Combinations

Image and caption.

Code
<script setup lang="ts">
    import { Skeleton } from '@fibrous/semi-ui-vue';
</script>

<template>
    <Skeleton loading>
        <template #placeholder>
            <div>
                <Skeleton.Image :style="{ width: '200px', height: '150px' }" />
                <Skeleton.Title :style="{ width: '120px', marginTop: '10px' }" />
            </div>
        </template>
        <img alt="avatar" height="150" src="/assets/placeholder/avatar-a.svg" />
        <h4>Semi UI</h4>
    </Skeleton>
</template>

Statistics.

Code
<script setup lang="ts">
    import { Descriptions, Skeleton } from '@fibrous/semi-ui-vue';

    const data = [{ key: '实际用户数量', value: '1,480,000' }];
</script>

<template>
    <Skeleton loading>
        <template #placeholder>
            <div>
                <Skeleton.Paragraph :rows="1" :style="{ width: '80px', marginBottom: '10px' }" />
                <Skeleton.Title :style="{ width: '120px' }" />
            </div>
        </template>
        <Descriptions :data="data" row />
    </Skeleton>
</template>

Avatar and title.

Code
<script setup lang="ts">
    import { Avatar, Skeleton } from '@fibrous/semi-ui-vue';
</script>

<template>
    <Skeleton loading>
        <template #placeholder>
            <div :style="{ display: 'flex', alignItems: 'center' }">
                <Skeleton.Avatar :style="{ marginRight: '12px' }" />
                <Skeleton.Title :style="{ width: '120px' }" />
            </div>
        </template>
        <Avatar color="blue" :style="{ marginRight: '12px' }">UI</Avatar>
        <span>Semi UI</span>
    </Skeleton>
</template>

Centered paragraphs and button.

Code
<script setup lang="ts">
    import type { StyleValue } from 'vue';
    import { Button, Skeleton } from '@fibrous/semi-ui-vue';

    const style: StyleValue = {
        display: 'flex',
        flexDirection: 'column',
        alignItems: 'center',
        width: '300px',
        marginBottom: '10px',
    };
</script>

<template>
    <Skeleton loading :style="{ textAlign: 'center' }">
        <template #placeholder>
            <div :style="style">
                <Skeleton.Paragraph :rows="3" :style="style" />
                <Skeleton.Button />
            </div>
        </template>
        <div :style="{ textAlign: 'center' }">
            <p>Hi, Bytedance dance dance.</p>
            <p>Hi, Bytedance dance dance.</p>
            <Button>Button</Button>
        </div>
    </Skeleton>
</template>

Avatar, headline and paragraph.

Code
<script setup lang="ts">
    import { Avatar, Skeleton } from '@fibrous/semi-ui-vue';

    const style = {
        display: 'flex',
        alignItems: 'flex-start',
    };
</script>

<template>
    <Skeleton loading>
        <template #placeholder>
            <div :style="style">
                <Skeleton.Avatar :style="{ marginRight: '12px' }" />
                <div>
                    <Skeleton.Title :style="{ width: '120px', marginBottom: '12px', marginTop: '12px' }" />
                    <Skeleton.Paragraph :rows="3" :style="{ width: '240px' }" />
                </div>
            </div>
        </template>
        <div :style="style">
            <Avatar color="blue" :style="{ marginRight: '12px' }">UI</Avatar>
            <div>
                <h3>Semi UI</h3>
                <p>Hi, Bytedance dance dance.</p>
                <p>Hi, Bytedance dance dance.</p>
                <p>Hi, Bytedance dance dance.</p>
            </div>
        </div>
    </Skeleton>
</template>

Table.

Code
<script setup lang="ts">
    import { Skeleton, Table, type ColumnProps } from '@fibrous/semi-ui-vue';

    const columns: ColumnProps[] = [
        {
            title: 'Name',
            dataIndex: 'name',
        },
        {
            title: 'Age',
            dataIndex: 'age',
        },
        {
            title: 'Address',
            dataIndex: 'address',
        },
    ];

    const dataSource = [
        {
            key: '1',
            name: 'John Brown',
            age: 32,
            address: 'New York No. 1 Lake Park, New York No. 1 Lake Park',
        },
        {
            key: '2',
            name: 'Jim Green',
            age: 42,
            address: 'London No. 1 Lake Park',
        },
        {
            key: '3',
            name: 'Joe Black',
            age: 32,
            address: 'Sidney No. 1 Lake Park',
        },
        {
            key: '4',
            name: 'Disabled User',
            age: 99,
            address: 'Sidney No. 1 Lake Park',
        },
    ];

    const placeholderColumns: ColumnProps[] = [1, 2, 3].map((key) => ({ dataIndex: `${key}` }));
    const placeholderDataSource = [1, 2, 3, 4].map((key) => ({ key }));
</script>

<template>
    <Skeleton loading>
        <template #placeholder>
            <div :style="{ position: 'relative' }">
                <Table :columns="placeholderColumns" :dataSource="placeholderDataSource" :pagination="false" :style="{ backgroundColor: 'var(--semi-color-bg-1)' }">
                    <template #headerCell>
                        <Skeleton.Title :style="{ width: '0' }" />
                    </template>
                    <template #cell="{ column }">
                        <Skeleton.Paragraph :rows="1" :style="{ width: `${50 * Number(column.dataIndex)}px` }" />
                    </template>
                </Table>
                <div :style="{ position: 'absolute', left: 0, right: 0, top: 0, bottom: 0 }" />
            </div>
        </template>
        <div>
            <Table :columns="columns" :dataSource="dataSource" :pagination="false" />
        </div>
    </Skeleton>
</template>

Animated Loading

Use active property to display animated loading effects.

Code
<script setup lang="ts">
    import { Avatar, Skeleton } from '@fibrous/semi-ui-vue';

    const style = {
        display: 'flex',
        alignItems: 'flex-start',
    };
</script>

<template>
    <Skeleton active loading>
        <template #placeholder>
            <div :style="style">
                <Skeleton.Avatar :style="{ marginRight: '12px' }" />
                <div>
                    <Skeleton.Title :style="{ width: '120px', marginBottom: '12px', marginTop: '12px' }" />
                    <Skeleton.Paragraph :rows="3" :style="{ width: '240px' }" />
                </div>
            </div>
        </template>
        <div :style="style">
            <Avatar color="blue" :style="{ marginRight: '12px' }">UI</Avatar>
            <div>
                <h3>Semi UI</h3>
                <p>Hi, Bytedance dance dance.</p>
                <p>Hi, Bytedance dance dance.</p>
                <p>Hi, Bytedance dance dance.</p>
            </div>
        </div>
    </Skeleton>
</template>

API reference

Skeleton

Props

PropertyDescriptionTypeDefault
activeWhether to show the animated loading effect.booleansemiGlobalDefault("Skeleton", "active")
loadingWhen true, the placeholder slot is shown. Otherwise the default slot is shown.booleansemiDefault("Skeleton", "loading", true)

Events

EventDescriptionCallback parameters
None

Slots

SlotDescriptionScope parameters
defaultContent shown once loading ends.any
placeholderElements shown while loading.any

Exposes

NameDescriptionType
None

class, style and native attributes fall through attrs onto the placeholder root element. When loading is false the component renders only the default slot and has no root element, so attrs are dropped together with the placeholder. The root's aria-busy="true" cannot be switched off through attrs.

Skeleton.Avatar

Props

PropertyDescriptionTypeDefault
sizeSize of the avatar placeholder.Type"medium" | "small" | "extra-extra-small" | "extra-small" | "large" | "extra-large""medium"
shapeShape of the avatar placeholder: circle or square."circle" | "square""circle"
prefixClsClass name prefix, semi-skeleton by default.stringundefined
typeReplaces the type of the block, which also names its class. Unless it is avatar, size and shape have no effect.stringundefined

Events

EventDescriptionCallback parameters
None

Slots

SlotDescriptionScope parameters
None

Exposes

NameDescriptionType
None

class, style and native attributes fall through attrs onto the placeholder block.

Skeleton.Image

Props

PropertyDescriptionTypeDefault
prefixClsClass name prefix, semi-skeleton by default.stringundefined
typeReplaces the type of the block, which also names its class.stringundefined

Events

EventDescriptionCallback parameters
None

Slots

SlotDescriptionScope parameters
None

Exposes

NameDescriptionType
None

Skeleton.Title

Props

PropertyDescriptionTypeDefault
prefixClsClass name prefix, semi-skeleton by default.stringundefined
typeReplaces the type of the block, which also names its class.stringundefined

Events

EventDescriptionCallback parameters
None

Slots

SlotDescriptionScope parameters
None

Exposes

NameDescriptionType
None

Skeleton.Button

Props

PropertyDescriptionTypeDefault
prefixClsClass name prefix, semi-skeleton by default.stringundefined
typeReplaces the type of the block, which also names its class.stringundefined

Events

EventDescriptionCallback parameters
None

Slots

SlotDescriptionScope parameters
None

Exposes

NameDescriptionType
None

Skeleton.Image, Skeleton.Title and Skeleton.Button have no size or shape; class, style and native attributes fall through attrs onto the placeholder block.

Skeleton.Paragraph

Props

PropertyDescriptionTypeDefault
prefixClsClass name prefix, semi-skeleton by default.stringcssClasses.PREFIX
rowsNumber of rows in the placeholder paragraph.number4

Events

EventDescriptionCallback parameters
None

Slots

SlotDescriptionScope parameters
None

Exposes

NameDescriptionType
None

class, style and native attributes fall through attrs onto the root ul element.

Accessibility

  • While loading, the outer element of Skeleton carries aria-busy="true", and the placeholder blocks and paragraph carry aria-hidden="true", so screen readers only know that content is loading. An aria-hidden passed by the caller takes precedence on a placeholder block; aria-busy cannot be switched off.
  • Once loading ends, the children are rendered directly and Skeleton adds no outer element.

Content Guidelines

  • Unchanged fixed content directly displays fixed content, and variable content is displayed using skeleton screen

Design Tokens

These are the component's internal Sass variables and cannot be overridden. To customize styles, override the --semi-* CSS variables (see the Design Token page) or write unlayered CSS rules.
Sass variableValueUsage
$animation_duration-skeleton-highlight1400ms骨架屏高亮动画时长
$animation_function-skeleton-highlightease
$color-skeleton_default-bg-defaultvar(--semi-color-fill-0)骨架屏背景色 - 默认
$color-skeleton_loading-gradient-bg-activevar(--semi-color-fill-1)骨架屏背景色 - 动画开启后
$width-skeleton_avatar_extra_extra_small20px头像骨架屏尺寸 - 极小
$width-skeleton_avatar_extra_small24px头像骨架屏尺寸 - 超小
$width-skeleton_avatar_small32px头像骨架屏尺寸 - 小
$width-skeleton_avatar_medium48px头像骨架屏尺寸 - 中
$width-skeleton_avatar_large72px头像骨架屏尺寸 - 大
$width-skeleton_avatar_extra_large128px头像骨架屏尺寸 - 超大
$width-skeleton_button115px按钮骨架屏宽度
$height-skeleton_li16px段落骨架屏高度
$height-skeleton_title$height-control-small标题骨架屏高度
$height-skeleton_button$height-control-default按钮骨架屏高度
$spacing-skeleton_li-marginBottom10px按钮骨架屏高度
$radius-skeleton_itemvar(--semi-border-radius-small)骨架屏圆角
$radius-skeleton_livar(--semi-border-radius-small)骨架屏圆角