Vue 3 — Spinner Component
How and why do we need spinners? And how can we create them in Vue 3?
We see them everywhere on every website (almost) we visit. A visual indicator, showing that that the system is doing something. It could look something like this.
And that's all it is. It’s a design “trick”, to keep the users on your site while data is being fetched in the background. It’s meant to communicate that the system is not broken, but simply waiting for something to display the data you need.
Its also sometimes referred to as a loader, even though that word would confuse some people
Why?
These spinners/loaders are necessary to let the user know that something is actually happening. If you don't have these components in your system, some users will end up leaving your site while you are fetching the data.
Example
All frameworks will have different ways of implementing this, but the general idea is the same. We are going to need a component that does the actual visual work of the spinner + a way to decide whether or not that component should be shown.
A basic component could look like this:
<script setup>
</script>
<template>
<div class="showbox">
<div class="loader">
<svg class="circular" viewBox="25 25 50 50">
<circle class="path" cx="50" cy="50" r="20" fill="none" stroke-width="2" stroke-miterlimit="10" />
</svg>
</div>
</div>
</template>
<style scoped>
.loader {
position: relative;
margin: 0 auto;
width: 100px;
}
.loader:before {
content: "";
display: block;
padding-top: 100%;
}
.circular {
-webkit-animation: rotate 2s linear infinite;
animation: rotate 2s linear infinite;
height: 100%;
transform-origin: center center;
width: 100%;
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
margin: auto;
}
.path {
stroke-dasharray: 1, 200;
stroke-dashoffset: 0;
-webkit-animation: dash 1.5s ease-in-out infinite, color 6s ease-in-out infinite;
animation: dash 1.5s ease-in-out infinite, color 6s ease-in-out infinite;
stroke-linecap: round;
}
@-webkit-keyframes rotate {
100% {
transform: rotate(360deg);
}
}
@keyframes rotate {
100% {
transform: rotate(360deg);
}
}
@-webkit-keyframes dash {
0% {
stroke-dasharray: 1, 200;
stroke-dashoffset: 0;
}
50% {
stroke-dasharray: 89, 200;
stroke-dashoffset: -35px;
}
100% {
stroke-dasharray: 89, 200;
stroke-dashoffset: -124px;
}
}
@keyframes dash {
0% {
stroke-dasharray: 1, 200;
stroke-dashoffset: 0;
}
50% {
stroke-dasharray: 89, 200;
stroke-dashoffset: -35px;
}
100% {
stroke-dasharray: 89, 200;
stroke-dashoffset: -124px;
}
}
@-webkit-keyframes color {
100%,
0% {
stroke: #d62d20;
}
40% {
stroke: #0057e7;
}
66% {
stroke: #008744;
}
80%,
90% {
stroke: #ffa700;
}
}
@keyframes color {
100%,
0% {
stroke: #d62d20;
}
40% {
stroke: #0057e7;
}
66% {
stroke: #008744;
}
80%,
90% {
stroke: #ffa700;
}
}
body {
background-color: #eee;
}
.showbox {
top: 0;
bottom: 0;
left: 0;
right: 0;
padding: 5%;
}</style>
That will generate something looking like this:
You can easily change this component to be as advanced or as simple as you want to.
Now that we have the component we can freely import it wherever we need it in our application.
I am using a previous example found here, which simply fetches some data from an API. Now we want to show a spinner while we are waiting for the API response.
To use the component in the component showing the blog posts we can do something like this:
<script setup>
import { usePostsStore } from './../stores/posts'
import { useTabStore } from './../stores/tab'
import Spinner from './spinner.vue'
import { computed } from 'vue'
import MediumPreview from './blogDesigns/MediumPreview.vue'
import MediumPreview2 from './blogDesigns/MediumPreview2.vue'
const componentMap = { MediumPreview, MediumPreview2 }
const tabStore = useTabStore()
const postStore = usePostsStore()
const loading = computed(() => {
return postStore.posts.length === 0
})
</script>
<template>
<div class="container">
<Spinner v-if="loading"></spinner>
<KeepAlive v-else>
<Transition name="translate" mode="out-in" v-for="(post) in postStore.posts" :key="post.title">
<component :is="componentMap[tabStore.tab]" :post="post" />
</Transition>
</KeepAlive>
</div>
</template>
The mechanism is rather simple:
- We import the spinner component
- We create a computed value, that will indicate if we are waiting for a response from the API
- Depending on the computed value we will either show the spinner or the list of blog posts.
For now, I have left out the case where an error occurs, so in this example we only take the simplest use case into account.
Conclusion
Spinners are nothing but a visual trick. It makes it easier for the user to trust that something is happening. The devil is in the details, and that's why we need it. Is the type of component, that a normal user would be excused for leaving out if he/she was tasked to list all the components in an application.
The result before implementing the spinner is this →
While the result after is this →
The full repo can be found here
And the live demo can be seen here
You might also like:
In Plain English 🚀
Thank you for being a part of the In Plain English community! Before you go:
- Be sure to clap and follow the writer ️👏️️
- Follow us: X | LinkedIn | YouTube | Discord | Newsletter
- Visit our other platforms: Stackademic | CoFeed | Venture
- More content at PlainEnglish.io