Vue Skeletor
Vue Skeletor is an adaptive skeleton loading component library specifically designed for Vue 3 applications, currently at version 1.0.6. It aims to simplify the creation of loading states by providing components that automatically adjust to the typography and layout of existing content. Unlike traditional skeleton libraries where developers manually define the size and position of skeleton elements, Vue Skeletor allows direct injection into existing component structures, automatically mimicking text styles. It offers both a component (`Skeletor`) for direct use in templates and a plugin (`VueSkeletor`) for global configuration, including dynamic runtime adjustments via the `useSkeletor` composable. The library focuses on reducing boilerplate and maintenance overhead associated with keeping skeleton loaders in sync with UI changes.
Common errors
-
[Vue warn]: Failed to resolve component: Skeletor
cause The `Skeletor` component has not been registered either locally within the component where it's used or globally within the Vue application instance.fixLocally register the component: `import { Skeletor } from 'vue-skeletor'; export default { components: { Skeletor } }` or globally: `import { Skeletor } from 'vue-skeletor'; app.component(Skeletor.name, Skeletor);` -
TypeError: Cannot read properties of undefined (reading 'shimmer')
cause Attempting to use the `useSkeletor` composable to access global configuration before the `VueSkeletor` plugin has been registered with the Vue application instance.fixEnsure `app.use(VueSkeletor, { shimmer: false })` (or similar) is called in your main application entry file (e.g., `main.js`/`main.ts`) before any components that use `useSkeletor` are mounted. -
Skeletons appear as unstyled blocks or text, without the shimmer animation.
cause The required CSS stylesheet for `vue-skeletor` components has not been imported into the application.fixAdd `import 'vue-skeletor/dist/vue-skeletor.css';` to your primary application entry file (e.g., `main.js` or `main.ts`) to ensure the styles are loaded.
Warnings
- gotcha When the `height` prop is explicitly set on a `<Skeletor />` component, it automatically renders as a block-level rectangle (`display: block`). This means it will no longer adapt to the typography or layout of its parent element, as it's intended for fixed-size elements like images or buttons. This behavior is by design but can be a surprise if you expect text adaptability.
- gotcha The visual styles for `vue-skeletor` components are not automatically bundled with the component logic. For the skeletons to render correctly with their characteristic shimmer effect and background, the library's CSS must be explicitly imported into your application.
- gotcha The `Skeletor` component is provided as a named export, whereas the `VueSkeletor` plugin (used with `app.use()`) is a default export. Incorrect import statements can lead to Vue failing to resolve the component or the plugin being undefined.
Install
-
npm install vue-skeletor -
yarn add vue-skeletor -
pnpm add vue-skeletor
Imports
- Skeletor
import Skeletor from 'vue-skeletor';
import { Skeletor } from 'vue-skeletor'; - VueSkeletor
import { VueSkeletor } from 'vue-skeletor';import VueSkeletor from 'vue-skeletor';
- useSkeletor
import useSkeletor from 'vue-skeletor';
import { useSkeletor } from 'vue-skeletor'; - Styles
import 'vue-skeletor/dist/vue-skeletor.css';
Quickstart
<!-- App.vue -->
<script setup lang="ts">
import { ref, onMounted } from 'vue';
import { Skeletor } from 'vue-skeletor';
import 'vue-skeletor/dist/vue-skeletor.css'; // Make sure styles are imported
interface Post {
img: string;
title: string;
text: string;
}
const post = ref<Post | null>(null);
const isPostLoading = ref(true);
onMounted(() => {
// Simulate API call
setTimeout(() => {
post.value = {
img: 'https://via.placeholder.com/200/cccccc/ffffff?text=Post+Image',
title: 'My Awesome Blog Post Title',
text: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.'
};
isPostLoading.value = false;
}, 2000);
});
</script>
<template>
<div class="post-container" :style="{ maxWidth: '600px', margin: '20px auto', padding: '20px', border: '1px solid #eee', borderRadius: '8px', boxShadow: '0 2px 4px rgba(0,0,0,0.1)' }">
<div class="post__image" :style="{ marginBottom: '15px' }">
<img
v-if="post"
:src="post.img"
height="200"
:style="{ width: '100%', display: 'block', borderRadius: '4px' }"
/>
<Skeletor v-else-if="isPostLoading" height="200" :style="{ borderRadius: '4px' }"/>
</div>
<div class="post__title" :style="{ fontSize: '2em', fontWeight: 'bold', marginBottom: '10px' }">
<template v-if="post">
{{ post.title }}
</template>
<Skeletor v-else-if="isPostLoading"/>
</div>
<div class="post__text" :style="{ lineHeight: '1.6', color: '#555' }">
<template v-if="post">
{{ post.text }}
</template>
<template v-else-if="isPostLoading">
<Skeletor v-for="i in 5" :key="i" :style="{ marginBottom: '8px' }"/>
</template>
</div>
</div>
</template>
<style>
/* Basic styles for demonstration, usually in a global or scoped stylesheet */
body {
font-family: Arial, sans-serif;
background-color: #f9f9f9;
margin: 0;
padding: 0;
}
</template>