← Build logs
FrontendJuly 17, 2026

Solving the Blank Screen Problem on Initial App Load in the Frontend

Seeing just a blank white screen when the app first opens feels a bit bare, right? I wanted to give users a good first impression, so I decided to add something that reflects our app's identity.

Attempts and Pitfalls

Initially, I tried to implement a feature where our mascot, 'Riel', would appear and greet the user on an initial screen, along with a growth bar.

<!-- initial_screen.html -->
<div class="splash-screen">
  <img src="/assets/riel_greeting.png" alt="Riel greeting">
  <div class="progress-bar-container">
    <div class="progress-bar" style="width: 0%;"></div>
  </div>
</div>

However, it turned out to be trickier than I expected. I kept running into an issue where the screen would briefly flash empty before Riel appeared. It was like Riel couldn't quite time her entrance.

// App.vue
<script setup>
import { ref, onMounted } from 'vue';
import RielGreeting from './components/RielGreeting.vue';

const showRiel = ref(false);
const growthProgress = ref(0);

onMounted(() => {
  setTimeout(() => {
    showRiel.value = true; // Let's show Riel!
  }, 500); // Appear after 0.5 seconds

  const interval = setInterval(() => {
    if (growthProgress.value < 100) {
      growthProgress.value += 10;
    } else {
      clearInterval(interval);
    }
  }, 200);
});
</script>

<template>
  <div id="app">
    <div v-if="!showRiel" class="initial-blank-screen"></div>
    <riel-greeting v-if="showRiel" :progress="growthProgress" />
  </div>
</template>

<style>
.initial-blank-screen {
  width: 100%;
  height: 100vh;
  background-color: white; /* White background */
}
</style>

Even though I was clearly setting showRiel to true, a blank white screen would briefly appear before rendering. This happened even with the setTimeout delay.

The Cause

It turned out to be a problem with Vue's reactivity system and component rendering order. Because of the v-if="!showRiel" condition, the blank screen was being rendered briefly before the riel-greeting component was mounted. Then, showRiel would change to true, and the riel-greeting component would appear. The setTimeout, it seems, doesn't actually prevent the rendering itself.

The Solution

The fix was surprisingly simple. Instead of having a separate blank screen, I modified the riel-greeting component itself to appear blank initially, and then reveal its content when the data was ready.

<!-- components/RielGreeting.vue -->
<script setup>
import { computed } from 'vue';

const props = defineProps({
  progress: Number
});

const progressBarWidth = computed(() => `${props.progress}%`);
</script>

<template>
  <div class="riel-container">
    <div v-if="progress === 0" class="riel-placeholder">
      <!-- Appears as a blank screen during loading -->
    </div>
    <template v-else>
      <img src="/assets/riel_greeting.png" alt="Riel greeting" class="riel-image">
      <div class="progress-bar-wrapper">
        <div class="progress-bar" :style="{ width: progressBarWidth }"></div>
      </div>
    </template>
  </div>
</template>

<style scoped>
.riel-container {
  width: 100%;
  height: 100vh;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  background-color: #f0f0f0; /* Default background color */
}

.riel-placeholder {
  width: 100%;
  height: 100%;
  background-color: white; /* White initially */
}

.riel-image {
  width: 150px;
  height: 150px;
  margin-bottom: 20px;
}

.progress-bar-wrapper {
  width: 200px;
  height: 10px;
  background-color: #ddd;
  border-radius: 5px;
  overflow: hidden;
}

.progress-bar {
  height: 100%;
  background-color: #4CAF50;
  transition: width 0.3s ease-in-out;
}
</style>
// App.vue (Modified parts)
<script setup>
import { ref, onMounted } from 'vue';
import RielGreeting from './components/RielGreeting.vue';

const growthProgress = ref(0);

onMounted(() => {
  const interval = setInterval(() => {
    if (growthProgress.value < 100) {
      growthProgress.value += 10;
    } else {
      clearInterval(interval);
    }
  }, 200);
});
</script>

<template>
  <div id="app">
    <riel-greeting :progress="growthProgress" />
  </div>
</template>

Now, the riel-greeting component itself decides whether to show its content based on the progress value. When progress is 0, riel-placeholder is rendered, making it look like a blank screen. As progress increases, Riel's image and the growth bar gradually appear.

Results

  • Provided users with a friendly character greeting experience ('Riel') upon the app's initial load.
  • Added a visual element that reinforces the app's identity.
  • No more blank screens during loading.

Takeaways — Avoiding the Same Pitfalls

  • [ ] Double-check your understanding of component rendering order and how v-if works in Vue.
  • [ ] Remember that setTimeout delays the execution of a callback, it doesn't prevent rendering itself.
  • [ ] For initial loading blank screens, it's often better to manage the loading state within the component itself and show a placeholder until data is ready.
  • [ ] Consider using characters or symbolic elements to create a more engaging first impression for your app.