Back to skills
SKILL.md
N Skeleton
ASecuritySkeleton component for displaying loading placeholders. Invoke when user needs to show content loading states with animated placeholders in Naive UI.
- 8 stars
- 0 votes
- 0 copies
- 2 views
- Added September 11, 2026
Works with
Security analysis
100/100npx -y skills add jiaiyan/naive-ui-skills --skill n-skeleton --agent claude-codeAre you the author of N Skeleton?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/jiaiyan-n-skeleton)---
name: "n-skeleton"
description: "Skeleton component for displaying loading placeholders. Invoke when user needs to show content loading states with animated placeholders in Naive UI."
metadata:
author: jiaiyan
version: "1.0.0"
---
# Naive UI Skeleton Component
Skeleton component for displaying animated loading placeholders while content is being loaded.
## When to Use
Use this component when:
- **Content loading**: Display placeholders while data is being fetched
- **Progressive loading**: Show skeleton during lazy-loaded content
- **Better UX**: Reduce perceived loading time with animated placeholders
- **Layout stability**: Prevent layout shift during content load
## When to Invoke
Invoke this skill when:
- User needs to show loading placeholders
- User wants to create skeleton screens
- User needs different skeleton shapes (text, box, circle)
- User wants to match skeleton size with actual components
- User asks about loading state UI
## Features
- **Text Skeleton**: Animated text placeholders
- **Shape Variants**: Box, circle, and rounded shapes
- **Size Options**: small, medium, and large sizes
- **Repeat Function**: Repeat skeleton elements easily
- **Animation Control**: Enable or disable animation
- **Custom Dimensions**: Set custom width and height
## API Reference
### Skeleton Props
| Name | Type | Default | Description |
|------|------|---------|-------------|
| text | `boolean` | `false` | Text skeleton style. |
| round | `boolean` | `false` | Round skeleton style. |
| circle | `boolean` | `false` | Circle skeleton style. |
| height | `string \| number` | `undefined` | Skeleton height. |
| width | `string \| number` | `undefined` | Skeleton width. |
| size | `'small' \| 'medium' \| 'large'` | `undefined` | Skeleton size. |
| repeat | `string \| number` | `1` | Repeat frequency for text skeletons. |
| animated | `boolean` | `true` | Whether to enable animation. |
| sharp | `boolean` | `true` | Whether to display as right-angled skeleton. |
## Basic Usage
### Text Skeleton
```vue
<template>
<n-skeleton text :repeat="2" />
<n-skeleton text style="width: 60%" />
</template>
```
### Box Skeleton
```vue
<template>
<n-space vertical>
<n-skeleton height="40px" width="33%" />
<n-skeleton height="40px" width="66%" :sharp="false" />
<n-skeleton height="40px" round />
<n-skeleton height="40px" circle />
</n-space>
</template>
```
### Size Matching
```vue
<template>
<n-space vertical>
<n-space>
<n-switch v-model:value="loading" />
Loading
</n-space>
<n-space>
<n-skeleton v-if="loading" :width="146" :sharp="false" size="medium" />
<n-button v-else>Won't you fly high</n-button>
<n-skeleton v-if="loading" :width="132" round size="medium" />
<n-button v-else round>free bird, yeah</n-button>
<n-skeleton v-if="loading" circle size="medium" />
<n-button v-else circle>?</n-button>
</n-space>
</n-space>
</template>
<script setup>
import { ref } from 'vue'
const loading = ref(true)
</script>
```
### No Animation
```vue
<template>
<n-skeleton text :animated="false" :repeat="3" />
</template>
```
## Common Patterns
### Card Skeleton
```vue
<template>
<n-card v-if="loading" style="width: 300px;">
<n-skeleton text :repeat="2" />
<n-skeleton text style="width: 60%" />
<template #footer>
<n-skeleton text style="width: 100px" />
</template>
</n-card>
<n-card v-else style="width: 300px;" :title="data.title">
{{ data.content }}
<template #footer>
<n-button type="primary">Action</n-button>
</template>
</n-card>
</template>
<script setup>
import { ref, reactive } from 'vue'
const loading = ref(true)
const data = reactive({
title: 'Card Title',
content: 'Card content goes here.'
})
</script>
```
### List Skeleton
```vue
<template>
<n-list v-if="loading">
<n-list-item v-for="i in 3" :key="i">
<template #prefix>
<n-skeleton circle size="medium" />
</template>
<n-skeleton text style="width: 200px" />
</n-list-item>
</n-list>
<n-list v-else>
<n-list-item v-for="item in items" :key="item.id">
<template #prefix>
<n-avatar :src="item.avatar" />
</template>
{{ item.name }}
</n-list-item>
</n-list>
</template>
<script setup>
import { ref } from 'vue'
const loading = ref(true)
const items = ref([
{ id: 1, name: 'Item 1', avatar: '/avatar1.jpg' },
{ id: 2, name: 'Item 2', avatar: '/avatar2.jpg' },
{ id: 3, name: 'Item 3', avatar: '/avatar3.jpg' }
])
</script>
```
### Table Skeleton
```vue
<template>
<n-data-table v-if="!loading" :columns="columns" :data="data" />
<div v-else class="table-skeleton">
<n-skeleton v-for="i in 5" :key="i" text :repeat="4" style="margin-bottom: 16px;" />
</div>
</template>
<script setup>
import { ref } from 'vue'
const loading = ref(true)
const columns = []
const data = []
</script>
<style scoped>
.table-skeleton {
padding: 16px;
}
</style>
```
### Article Skeleton
```vue
<template>
<div class="article-skeleton" v-if="loading">
<n-skeleton text style="width: 50%; height: 32px; margin-bottom: 24px;" />
<n-skeleton text :repeat="4" />
<n-skeleton text style="width: 70%" />
<n-skeleton height="200px" style="margin: 24px 0;" />
<n-skeleton text :repeat="3" />
<n-skeleton text style="width: 60%" />
</div>
<article v-else>
<h1>{{ article.title }}</h1>
<p>{{ article.content }}</p>
<img :src="article.image" alt="Article image" />
<p>{{ article.moreContent }}</p>
</article>
</template>
<script setup>
import { ref, reactive } from 'vue'
const loading = ref(true)
const article = reactive({
title: 'Article Title',
content: 'Article content...',
image: '/article.jpg',
moreContent: 'More content...'
})
</script>
```
### Profile Card Skeleton
```vue
<template>
<n-card style="width: 300px;">
<div class="profile-skeleton" v-if="loading">
<n-skeleton circle :size="80" style="margin: 0 auto 16px;" />
<n-skeleton text style="width: 60%; margin: 0 auto 8px;" />
<n-skeleton text style="width: 80%; margin: 0 auto;" />
<n-divider />
<n-space justify="center">
<n-skeleton :width="80" :height="32" round />
<n-skeleton :width="80" :height="32" round />
</n-space>
</div>
<div class="profile" v-else>
<n-avatar :src="user.avatar" :size="80" style="margin: 0 auto 16px;" />
<h3 style="text-align: center;">{{ user.name }}</h3>
<p style="text-align: center; color: #999;">{{ user.bio }}</p>
<n-divider />
<n-space justify="center">
<n-button size="small">Follow</n-button>
<n-button size="small">Message</n-button>
</n-space>
</div>
</n-card>
</template>
<script setup>
import { ref, reactive } from 'vue'
const loading = ref(true)
const user = reactive({
name: 'John Doe',
bio: 'Software Developer',
avatar: '/avatar.jpg'
})
</script>
```
### Form Skeleton
```vue
<template>
<n-form v-if="!loading" :model="formData">
<n-form-item label="Name">
<n-input v-model:value="formData.name" />
</n-form-item>
<n-form-item label="Email">
<n-input v-model:value="formData.email" />
</n-form-item>
<n-button type="primary">Submit</n-button>
</n-form>
<div v-else class="form-skeleton">
<n-skeleton text style="width: 80px; margin-bottom: 8px;" />
<n-skeleton :height="34" style="margin-bottom: 16px;" />
<n-skeleton text style="width: 80px; margin-bottom: 8px;" />
<n-skeleton :height="34" style="margin-bottom: 16px;" />
<n-skeleton :width="100" :height="34" round />
</div>
</template>
<script setup>
import { ref, reactive } from 'vue'
const loading = ref(true)
const formData = reactive({
name: '',
email: ''
})
</script>
```
### Grid Skeleton
```vue
<template>
<n-grid v-if="loading" :cols="3" :x-gap="16" :y-gap="16">
<n-gi v-for="i in 6" :key="i">
<n-card>
<n-skeleton height="150px" style="margin-bottom: 12px;" />
<n-skeleton text style="width: 70%; margin-bottom: 8px;" />
<n-skeleton text style="width: 50%;" />
</n-card>
</n-gi>
</n-grid>
<n-grid v-else :cols="3" :x-gap="16" :y-gap="16">
<n-gi v-for="item in items" :key="item.id">
<n-card>
<img :src="item.image" style="width: 100%; height: 150px; object-fit: cover;" />
<h4>{{ item.title }}</h4>
<p>{{ item.description }}</p>
</n-card>
</n-gi>
</n-grid>
</template>
<script setup>
import { ref } from 'vue'
const loading = ref(true)
const items = ref([])
</script>
```
## Best Practices
1. **Match skeleton dimensions**: Size skeleton to match actual content
```vue
<n-skeleton :width="200" :height="34" /> <!-- Match button size -->
```
2. **Use appropriate shapes**: Match skeleton shape to content type
```vue
<n-skeleton circle /> <!-- For avatars -->
<n-skeleton text /> <!-- For text -->
```
3. **Disable animation for static content**: Turn off animation when not needed
```vue
<n-skeleton :animated="false" />
```
4. **Use repeat for multiple lines**: Efficiently create multiple text lines
```vue
<n-skeleton text :repeat="3" />
```
5. **Maintain layout stability**: Ensure skeleton matches final content layout
```vue
<n-skeleton style="width: 100%; height: 200px;" />
```
6. **Use size prop for component matching**: Match skeleton size with components
```vue
<n-skeleton size="small" /> <!-- Matches small components -->
```
7. **Combine with loading states**: Use with conditional rendering
```vue
<n-skeleton v-if="loading" />
<div v-else>{{ content }}</div>
```
Attribution
Comments
Loading comments…
Newsletter
Get the weekly list of safe new skills, and the ones we pulled.