Skeleton
When loading data, and you need a rich experience for visual and interactions for your end users, you can choose skeleton
.
Basic usage
The basic skeleton.
<template>
<b-skeleton />
<br />
<b-skeleton style="--b-skeleton-circle-size: 96px">
<template #template>
<b-skeleton-item variant="circle" />
</template>
</b-skeleton>
</template>
Configurable Rows
You can configure the row numbers yourself, for more precise rendering effect, the actual rendered row number will always be 1 row more than the given number, that is because we are rendering a title row with 33% width of the others.
<template>
<b-skeleton :rows="5" />
</template>
Animation
We have provided a switch flag indicating whether showing the loading animation, called animated
when this is true, all children of el-skeleton
will show animation
<template>
<b-skeleton :rows="5" animated />
</template>
Customized Template
Element Plus only provides the most common template, sometimes that could be a problem, so you have a slot named template
to do that work.
Also we have provided different types skeleton unit that you can choose, for more detailed info, please scroll down to the bottom of this page to see the API description. Also, when building your own customized skeleton structure, you should be structuring them as closer to the real DOM as possible, which avoiding the DOM bouncing caused by the height difference.
<template>
<b-skeleton class="w-240px">
<template #template>
<b-skeleton-item variant="image" class="w-240px h-240px" />
<div class="mt-4">
<b-skeleton-item variant="h4" class="w-1/2" />
<div class="flex mt-4">
<b-skeleton-item variant="text" class="grow mr-4" />
<b-skeleton-item variant="text" class="w-3/10" />
</div>
</div>
</template>
</b-skeleton>
</template>
Loading state
When Loading
ends, we always need to show the real UI with data to our end users. with the attribtue loading
we can control whether showing the DOM. You can also use slot default
to structure the real DOM element.
<template>
<b-space vertical alignment="flex-start">
<div>
<label class="mr-4">Switch Loading</label>
<b-switch v-model="loading" />
</div>
<b-skeleton class="w-240px" :loading="loading" animated>
<template #template>
<b-skeleton-item variant="image" class="w-full h-240px" />
<div class="mt-4">
<b-skeleton-item variant="h4" class="w-1/2" />
<div class="flex mt-4">
<b-skeleton-item variant="text" class="mr-4 grow" />
<b-skeleton-item variant="text" class="w-3/10" />
</div>
</div>
</template>
<template #default>
<b-card
class="w-240px"
bordered
header-class="p-0 border-b-0"
body-class="p-4"
>
<template #header>
<img
class="h-240px w-full object-cover"
src="https://images.unsplash.com/photo-1579436938322-7898a4dc326d?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxzZWFyY2h8N3x8dGFtbWFpfGVufDB8fDB8fA%3D%3D&auto=format&fit=crop&w=480"
/>
</template>
<h4>Delicious hamburger</h4>
<div class="flex items-center mt-4">
<p class="text-sm b-c-neutral-7 grow mr-4">{{ currentDate }}</p>
<b-button ghost small>Operation</b-button>
</div>
</b-card>
</template>
</b-skeleton>
</b-space>
</template>
<script lang="ts" setup>
import { ref } from 'vue'
const loading = ref(true)
const currentDate = new Date().toDateString()
</script>
Rendering a list of data
Most of the time, skeleton is used as indicators of rendering a list of data which haven't been fetched from server yet, then we need to create a list of skeleton out of no where to make it look like it is loading, with count
attribute, you can control how many these templates you need to render to the browser.
TIP
We do not recommend rendering lots of fake UI to the browser, it will still cause the performance issue, it also costs longer to destroy the skeleton. Keep count
as small as it can be to make better user experience.
<template>
<b-space vertical alignment="flex-start">
<b-button small @click="setLoading">Click me to reload</b-button>
<div class="w-full">
<b-skeleton :loading="loading" animated :count="3">
<template #template>
<b-skeleton-item variant="image" class="w-223px h-160px" />
<div class="mt-4">
<b-skeleton-item variant="h4" class="w-1/2" />
<div class="flex mt-4">
<b-skeleton-item variant="text" class="grow mr-4" />
<b-skeleton-item variant="text" class="w-1/3" />
</div>
</div>
</template>
<template #default>
<b-row :gutter="4">
<b-col v-for="item in lists" :key="item.name">
<b-card bordered header-class="p-0 border-b-0" body-class="p-4">
<template #header>
<img class="h-240px w-full object-cover" :src="item.imgUrl" />
</template>
<h4>{{ item.name }}</h4>
<div class="flex items-center mt-4">
<p class="text-sm grow b-c-neutral-7 mr-4">
{{ currentDate }}
</p>
<b-button ghost small>Operation</b-button>
</div>
</b-card>
</b-col>
</b-row>
</template>
</b-skeleton>
</div>
</b-space>
</template>
<script lang="ts" setup>
import { onMounted, ref } from 'vue'
interface ListItem {
imgUrl: string
name: string
}
const loading = ref(true)
const lists = ref<ListItem[]>([])
const currentDate = new Date().toDateString()
const setLoading = () => {
loading.value = true
setTimeout(() => {
loading.value = false
}, 30000)
}
onMounted(() => {
loading.value = false
lists.value = [
{
imgUrl:
'https://images.unsplash.com/photo-1662622600433-f31acfd11e04?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=480&q=80',
name: 'Sea',
},
{
imgUrl:
'https://images.unsplash.com/photo-1579436938466-92a9ba75812f?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=480&q=80',
name: 'Field',
},
{
imgUrl:
'https://images.unsplash.com/photo-1541231587586-eaf981ae5fff?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=480&q=80',
name: 'Flower',
},
]
})
</script>
Avoiding rendering bouncing.
Sometimes API responds very quickly, when that happens, the skeleton just gets rendered to the DOM then it needs to switch back to real DOM, that causes the sudden flashy. To avoid such thing, you can use the throttle
attribute.
<template>
<b-space vertical alignment="flex-start">
<div>
<label class="mr-4">Switch Loading</label>
<b-switch v-model="loading" />
</div>
<b-skeleton class="w-240px" :loading="loading" animated :throttle="500">
<template #template>
<b-skeleton-item variant="image" class="w-full h-240px" />
<div class="mt-4">
<b-skeleton-item variant="h4" class="w-1/2" />
<div class="flex mt-4">
<b-skeleton-item variant="text" class="grow mr-4" />
<b-skeleton-item variant="text" class="w-1/3" />
</div>
</div>
</template>
<template #default>
<b-card
bordered
class="w-240px"
body-class="p-4"
header-class="p-0 border-b-0"
>
<template #header>
<img
class="h-240px w-full object-cover"
src="https://images.unsplash.com/photo-1579436938322-7898a4dc326d?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxzZWFyY2h8N3x8dGFtbWFpfGVufDB8fDB8fA%3D%3D&auto=format&fit=crop&w=480"
/>
</template>
<h4>Delicious hamburger</h4>
<div class="flex items-center mt-4">
<div class="text-sm b-c-neutral-7 mr-4 grow">
{{ currentDate }}
</div>
<b-button ghost small>Operation</b-button>
</div>
</b-card>
</template>
</b-skeleton>
</b-space>
</template>
<script lang="ts" setup>
import { ref } from 'vue'
const loading = ref(false)
const currentDate = new Date().toDateString()
</script>
Skeleton Attributes
Name | Description | Type | Default |
---|
animated | whether showing the animation | boolean | false |
count | how many fake items to render to the DOM | number | 1 |
loading | whether showing the real DOM | boolean | false |
rows | numbers of the row, only useful when no template slot were given | number | 3 |
throttle | Rendering delay in millseconds | number | 0 |
Skeleton Slots
Name | Description | Scope |
---|
default | Real rendering DOM | $attrs |
template | Custom rendering skeleton template | { key: number } |
Skeleton Item Attributes
Name | Description | Type | Default |
---|
variant | The current rendering skeleton type | p / text / h2 / h3 / h4 / caption / button / image / circle / rect | text |