The <VueListInitialLoader> component shows a loading indicator only during the very first data fetch. Once data has loaded successfully, it never shows again (even during subsequent page changes or filtering).
Basic usage
< template >
< VueList endpoint = "users" >
< VueListInitialLoader >
< div > Loading users... </ div >
</ VueListInitialLoader >
< VueListItems # default = " { items } " >
< div v-for = " user in items " : key = " user . id " >
{{ user . name }}
</ div >
</ VueListItems >
</ VueList >
</ template >
Props
This component has no props. It uses Vue’s inject to access the list state from the parent <VueList> component.
Behavior
Shows when : isInitialLoading === true (only during first fetch)
Hidden after : First successful data load
Never shows again : Even when paginating, filtering, or sorting
Hides <VueListItems> : Items won’t render until initial load completes
Slot
The default slot renders your custom loading UI:
< VueListInitialLoader >
<div class="loading-screen">
<div class="spinner"></div>
<p>Loading your data...</p>
</div>
</ VueListInitialLoader >
Examples
Skeleton loader
< VueListInitialLoader >
<div class="space-y-4">
<div class="skeleton-item" v-for="i in 5" :key="i">
<div class="skeleton-avatar"></div>
<div class="skeleton-text">
<div class="skeleton-line w-3/4"></div>
<div class="skeleton-line w-1/2"></div>
</div>
</div>
</div>
</ VueListInitialLoader >
< style >
.skeleton-line {
height : 12 px ;
background : linear-gradient ( 90 deg , #f0f0f0 25 % , #e0e0e0 50 % , #f0f0f0 75 % );
background-size : 200 % 100 % ;
animation : loading 1.5 s infinite ;
border-radius : 4 px ;
margin-bottom : 8 px ;
}
.skeleton-avatar {
width : 48 px ;
height : 48 px ;
background : linear-gradient ( 90 deg , #f0f0f0 25 % , #e0e0e0 50 % , #f0f0f0 75 % );
background-size : 200 % 100 % ;
animation : loading 1.5 s infinite ;
border-radius : 50 % ;
}
@keyframes loading {
0% { background-position : 200 % 0 ; }
100% { background-position : -200 % 0 ; }
}
</ style >
Centered spinner
< VueListInitialLoader >
<div class="flex items-center justify-center py-16">
<div class="text-center">
<svg class="animate-spin h-12 w-12 text-blue-500 mx-auto" fill="none" viewBox="0 0 24 24">
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
</svg>
<p class="mt-4 text-gray-600">Loading products...</p>
</div>
</div>
</ VueListInitialLoader >
Skeleton grid for products
< VueListInitialLoader >
<div class="grid grid-cols-3 gap-4">
<div class="skeleton-card" v-for="i in 9" :key="i">
<div class="skeleton-image"></div>
<div class="skeleton-line w-full"></div>
<div class="skeleton-line w-2/3"></div>
<div class="skeleton-line w-1/2"></div>
</div>
</div>
</ VueListInitialLoader >
< style >
.skeleton-card {
padding : 16 px ;
border : 1 px solid #e5e7eb ;
border-radius : 8 px ;
}
.skeleton-image {
height : 200 px ;
margin-bottom : 12 px ;
background : linear-gradient ( 90 deg , #f0f0f0 25 % , #e0e0e0 50 % , #f0f0f0 75 % );
background-size : 200 % 100 % ;
animation : loading 1.5 s infinite ;
border-radius : 4 px ;
}
.skeleton-line {
height : 12 px ;
margin-bottom : 8 px ;
background : linear-gradient ( 90 deg , #f0f0f0 25 % , #e0e0e0 50 % , #f0f0f0 75 % );
background-size : 200 % 100 % ;
animation : loading 1.5 s infinite ;
border-radius : 4 px ;
}
@keyframes loading {
0% { background-position : 200 % 0 ; }
100% { background-position : -200 % 0 ; }
}
</ style >
Skeleton table
< table class = "w-full" >
<thead>
<tr>
<th>Name</th>
<th>Email</th>
<th>Status</th>
</tr>
</thead>
<tbody>
<VueListInitialLoader>
<tr v-for="i in 10" :key="i">
<td><div class="skeleton-line w-32"></div></td>
<td><div class="skeleton-line w-48"></div></td>
<td><div class="skeleton-line w-20"></div></td>
</tr>
</VueListInitialLoader>
<VueListItems>
<template #item="{ item }">
<tr>
<td>{{ item.name }}</td>
<td>{{ item.email }}</td>
<td>{{ item.status }}</td>
</tr>
</template>
</VueListItems>
</tbody>
</ table >
Pulsing dots
< VueListInitialLoader >
<div class="flex items-center justify-center py-16">
<div class="flex gap-2">
<div class="w-3 h-3 bg-blue-500 rounded-full animate-bounce"></div>
<div class="w-3 h-3 bg-blue-500 rounded-full animate-bounce" style="animation-delay: 0.2s"></div>
<div class="w-3 h-3 bg-blue-500 rounded-full animate-bounce" style="animation-delay: 0.4s"></div>
</div>
</div>
</ VueListInitialLoader >
Full-screen loader
< VueListInitialLoader >
<div class="fixed inset-0 bg-white flex items-center justify-center">
<div class="text-center">
<svg class="animate-spin h-16 w-16 text-blue-500 mx-auto" viewBox="0 0 24 24">
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4" fill="none"></circle>
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z"></path>
</svg>
<h2 class="mt-4 text-xl font-semibold">Loading Dashboard</h2>
<p class="mt-2 text-gray-600">Please wait...</p>
</div>
</div>
</ VueListInitialLoader >
Difference from Loader
Feature VueListInitialLoader VueListLoader When it shows Only during first load During subsequent loads Hides items? Yes (items are hidden) No (items remain visible) Use case Initial data fetch Page changes, filtering, sorting Typical UI Full skeleton or placeholder Subtle overlay or indicator
Complete example
< template >
< VueList endpoint = "posts" : per-page = " 10 " >
<!-- Initial load: Show skeleton -->
< VueListInitialLoader >
< div class = "space-y-6" >
< div class = "post-skeleton" v-for = " i in 5 " : key = " i " >
< div class = "skeleton-line w-3/4 h-6" ></ div >
< div class = "skeleton-line w-full h-4 mt-2" ></ div >
< div class = "skeleton-line w-full h-4" ></ div >
< div class = "skeleton-line w-2/3 h-4" ></ div >
< div class = "flex gap-2 mt-4" >
< div class = "skeleton-line w-20 h-8" ></ div >
< div class = "skeleton-line w-20 h-8" ></ div >
</ div >
</ div >
</ div >
</ VueListInitialLoader >
<!-- After initial load: Show items -->
< VueListItems # default = " { items } " >
< div class = "space-y-6" >
< article v-for = " post in items " : key = " post . id " class = "post" >
< h2 > {{ post . title }} </ h2 >
< p > {{ post . excerpt }} </ p >
< div class = "flex gap-2" >
< span class = "badge" > {{ post . category }} </ span >
< span class = "text-sm text-gray-500" > {{ post . date }} </ span >
</ div >
</ article >
</ div >
</ VueListItems >
<!-- Subsequent loads: Show subtle loader -->
< VueListLoader >
< div class = "text-center py-4" >
< span class = "text-sm text-gray-500 animate-pulse" > Loading... </ span >
</ div >
</ VueListLoader >
< VueListPagination class = "mt-8" />
</ VueList >
</ template >
Use skeleton loaders that match your actual content layout for a smoother perceived loading experience.
The <VueListItems> component is automatically hidden while isInitialLoading is true, so you don’t need to manually control visibility.
Next steps
Loader Loading state for subsequent fetches
Empty state Handle empty results