URL: https://www.progressiverobot.com/ionic-ionic-4-vue-skeleton-text/

When we're loading asynchronous content, it's advised that you show the user some "skeleton" UI that gives the impression of content being loaded. Let's use ion-skeleton-text to show how we'd handle this inside of Ionic!

Inside of an Ionic project we'd traditionally have to accomplish this ourselves with CSS or a third party library. Thankfully, the latest update inside of Ionic 4.1 Hydrogen brings us ion-skeleton-text which we can use to display skeleton content.

Ionic Vue + Skeleton Text

skeleton illustration for: Ionic Vue + Skeleton Text

We'll be looking at this with the context of a Vue.js project, but as Ionic is built with Stencil, the underlying principles are framework-agnostic.

To get started, ensure you have Node.js installed on your machine. Then, run the following in your terminal:

				
					
$ npm install -g @vue/cli



$ vue create vue-ion-skeleton



> default project setup



$ npm install @ionic/core @ionic/vue

				
			

We then need to set up IonicVue inside of our project inside of main.js. We'll also be importing the basic styles that Ionic requires from @ionic/core

				
					
[label main.js]

import Vue from 'vue';

import App from './App.vue';

import '@ionic/core/css/core.css';

import '@ionic/core/css/ionic.bundle.css';



import IonicVue from '@ionic/vue';



Vue.use(IonicVue); 



Vue.config.productionTip = false;



new Vue({

 render : (h) => h(App)

}).$mount('#app');

				
			

At this stage, we can test that everything works correctly by creating a bare bones Ionic application inside of App.vue:

				
					
[label App.vue]

<template>

 <ion-app>

 <ion-header>

 <ion-toolbar color="danger">

 <ion-title>S C A R Y</ion-title>

 </ion-toolbar>

 </ion-header>

 <ion-content>

 <ion-card>

 <img src="https://images.unsplash.com/photo-1513681955987-968b5455d7d7?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=933&q=80"/>

 <ion-card-header>

 <ion-card-subtitle>Spooky, Scary</ion-card-subtitle>

 <ion-card-title>Skeleton</ion-card-title>

 </ion-card-header>

 <ion-card-content>

 Nescio brains an Undead zombies. Sicut malus putrid voodoo horror. Nigh tofth eliv ingdead.

 </ion-card-content>

 </ion-card>

 </ion-content>

 </ion-app>

</template>



<script>



export default {

 name: 'app',

}

</script>



<style>

#app {

 font-family: 'Avenir', Helvetica, Arial, sans-serif;

 -webkit-font-smoothing: antialiased;

 -moz-osx-font-smoothing: grayscale;

 text-align: center;

 color: #2c3e50;

 margin-top: 60px;

}

</style>

				
			

Now that we've created a <^>bare bones<^> application, let's move on to our ion-skeleton-text example:

Skeleton UI

For example's sake, let's say we wanted to load numerous spooky todos and each one comes from our API with varying data.

If our user was at a location with bad WiFi (<^>say, a coffee shop like the one I'm in right now<^>), it'd be futile to expect that this data will appear instantaneously.

What do we do? Display skeleton data of course!

Let's implement this inside of our application. We'll use the json-placeholder API to get data from an API and use ion-skeleton-text as a UI buffer until our data arrives.

				
					
&lt;template&gt;

 &lt;ion-app&gt;

 &lt;ion-header&gt;

 &lt;ion-toolbar color="danger"&gt;

 &lt;ion-title&gt;S C A R Y T O D O S&lt;/ion-title&gt;

 &lt;/ion-toolbar&gt;

 &lt;/ion-header&gt;

 &lt;ion-content&gt;

 &lt;ion-list v-if="todos.length &gt; 0"&gt;

 &lt;ion-item v-for="todo in todos" :key="todo.id"&gt;

 &lt;ion-label&gt;{{todo.title}}&lt;/ion-label&gt;

 &lt;/ion-item&gt;

 &lt;/ion-list&gt;

 &lt;ion-list v-else&gt;

 &lt;ion-item v-for="i in 20" :key="i"&gt;

 &lt;ion-label&gt;

 &lt;ion-skeleton-text animated&gt;

 &lt;/ion-skeleton-text&gt;

 &lt;/ion-label&gt;

 &lt;/ion-item&gt;

 &lt;/ion-list&gt;

 &lt;/ion-content&gt;

 &lt;/ion-app&gt;

&lt;/template&gt;



&lt;script&gt;



export default {

 name: 'app',

 data() {

 return {

 todos: []

 }

 },

 created() {

 setTimeout(

 () =&gt; (

 this.getDataFromAPI()

 ), 3000)

 },

 methods: {

 async getDataFromAPI() {

 try {

 const req = await fetch('https://jsonplaceholder.typicode.com/todos')

 this.todos = await req.json()

 }

 catch(e) {

 console.error(`Error: ${e}`)

 }

 }

 }

}

&lt;/script&gt;



&lt;style&gt;

#app {

 font-family: 'Avenir', Helvetica, Arial, sans-serif;

 -webkit-font-smoothing: antialiased;

 -moz-osx-font-smoothing: grayscale;

 text-align: center;

 color: #2c3e50;

 margin-top: 60px;

}



&lt;/style&gt;

				
			

Let's take a deep dive into what's happening here:

  1. Firstly, we're getting access to a list of todos by making a fetch call to the json-placeholder API. This is done via the getDataFromAPI method that we've created.
  1. We're then calling the getDataFromAPI method inside of the created hook inside of a setTimeout with a 3 second delay. This mirrors a moderate speed internet connection and gives us enough time to see our skeleton text in action.
  1. The use of ion-skeleton-text inside of our v-else block allows us to use the Ionic component that would otherwise <^>replace<^> the skeleton component, thus, keeping as much as the original styling as possible.

Note: We're taking advantage of the animated attribute within the ion-skeleton-text to animate this on screen. You'll see what it looks like when it's <^>not<^> animated later in this article.

This gives us the following magical piece of UI:

Other Examples

The great thing about the ion-skeleton-text component is that it's super flexible! We can use the width style attribute to change how it looks on screen.

Let's add a little bit of randomness to our ion-skeleton-text:

				
					
&lt;ion-list v-else&gt;

 &lt;ion-item v-for="i in 20" :key="i"&gt;

 &lt;ion-label&gt;

 &lt;ion-skeleton-text :style="`width: ${(80 + Math.random () * Math.floor(240))}px;`"&gt;

 &lt;/ion-skeleton-text&gt;

 &lt;/ion-label&gt;

 &lt;/ion-item&gt;

&lt;/ion-list&gt;

				
			

Summary

Tada! 🎉 Now we can make awesome "loading" UIs in places where the spinner doesn't make sense. It's a great way to replicate <^>how<^> text-based parts of your components will look white asynchronously loading data.

The source code for this article is available here: Source code