Chapter 72: Vue v-cloak Directive
V-cloak
This is not a flashy directive like v-for or v-model — it’s a tiny helper that solves one very specific but very visible problem:
The “Flash of Uncompiled Mustache” (also called FOUC — Flash of Unstyled Content — but for Vue templates)
The Problem v-cloak Solves (Real User Experience Issue)
When you open a Vue app for the first time (especially on slow networks or first paint), the browser downloads your JavaScript bundle → Vue boots up → compiles the templates → replaces {{ }} mustache expressions with real data.
During those few milliseconds (or longer on slow 3G/mobile), the user might see the raw template text:
|
0 1 2 3 4 5 6 7 |
Welcome, {{ user.name }}! Your balance: ₹{{ balance }} |
Instead of:
|
0 1 2 3 4 5 6 7 |
Welcome, Rahul! Your balance: ₹4,999 |
That raw {{ user.name }} flash looks extremely unprofessional — especially on landing pages, marketing sites, dashboards with prices/names/numbers.
v-cloak is the official Vue way to hide the element until Vue has finished compiling and replaced all the {{ }} placeholders.
How v-cloak Works (Step by Step)
-
You add v-cloak to any element (or the root element) that contains mustache expressions:
HTML0123456789<div id="app" v-cloak><h1>Welcome, {{ user.name }}!</h1><p>Balance: ₹{{ balance.toLocaleString() }}</p></div> -
Vue automatically adds a special attribute [v-cloak] to that element during compilation.
-
You write one CSS rule (usually in global styles):
CSS012345678[v-cloak] {display: none !important;} -
When Vue boots up and finishes compiling:
- It removes the [v-cloak] attribute from all elements that have it
- The element becomes visible instantly — now with real data instead of {{ }}
→ User never sees the raw mustache syntax
Real-World Example – Clean Landing Page
index.html (global styles + v-cloak)
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <title>Vue App</title> <style> [v-cloak] { display: none !important; } body { font-family: system-ui, sans-serif; margin: 0; padding: 2rem; background: #f8fafc; } .hero { max-width: 800px; margin: 0 auto; text-align: center; } .loading-skeleton { display: none; } [v-cloak] .loading-skeleton { display: block; } </style> </head> <body> <div id="app" v-cloak> <!-- Shown only while Vue is compiling --> <div class="loading-skeleton"> <h1 class="skeleton-text"></h1> <p class="skeleton-text"></p> </div> <!-- Real content – hidden until compiled --> <div class="hero"> <h1>Welcome, {{ user.name || 'Guest' }}!</h1> <p>Your current balance: ₹{{ balance.toLocaleString() || '0' }}</p> <button @click="refreshBalance">Refresh Balance</button> </div> </div> <script type="module" src="/src/main.ts"></script> </body> </html> |
src/main.ts (or App.vue)
|
0 1 2 3 4 5 6 7 8 9 |
import { createApp } from 'vue' import App from './App.vue' createApp(App).mount('#app') |
src/App.vue
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
<script setup lang="ts"> import { ref } from 'vue' // Simulate slow data load (real apps often have this delay) const user = ref({ name: '' }) const balance = ref(0) setTimeout(() => { user.value.name = 'Rahul from Nellore' balance.value = 4999 }, 800) // fake network delay </script> |
What the user sees
- First 0–800 ms: blank screen (because of [v-cloak] { display: none })
- After 800 ms: instantly clean content: “Welcome, Rahul from Nellore! Your current balance: ₹4,999”
No flash of {{ user.name }} or {{ balance }}
Bonus: Better UX with Skeleton + v-cloak
Many modern apps show skeleton loaders while Vue boots:
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
<div id="app" v-cloak> <!-- Skeleton shown while Vue compiles --> <div class="loading-skeleton"> <div class="skeleton-avatar"></div> <div class="skeleton-text long"></div> <div class="skeleton-text short"></div> </div> <!-- Real content – hidden until compiled --> <div class="real-content"> <img :src="user.avatar" class="avatar" /> <h1>Welcome, {{ user.name }}</h1> <p>Balance: ₹{{ balance.toLocaleString() }}</p> </div> </div> |
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
[v-cloak] .real-content { display: none; } [v-cloak] .loading-skeleton { display: block; } /* Skeleton styles */ .skeleton-avatar { width: 80px; height: 80px; border-radius: 50%; background: #e5e7eb; margin: 0 auto 1rem; } .skeleton-text { height: 1.2rem; background: #e5e7eb; border-radius: 4px; margin: 0.5rem 0; } .long { width: 80%; } .short { width: 50%; } |
→ User sees nice skeleton instead of blank screen or raw text
Quick Summary Table – v-cloak in 2026
| Question | Answer / Best Practice 2026 |
|---|---|
| What does it prevent? | Flash of uncompiled mustache {{ }} / raw template text |
| Where to put v-cloak? | On the root element (#app) or any element with {{ }} expressions |
| Required CSS? | [v-cloak] { display: none !important; } (global stylesheet) |
| Does it affect performance? | Zero — just a CSS rule + one attribute removal after compile |
| Still needed in 2026? | Yes — especially on slower mobile networks, first paint, marketing/landing pages |
| Alternative approaches? | Skeleton loaders + v-cloak, or SSR (Nuxt) which eliminates the flash completely |
Pro Tips from Real Projects (Nellore / Hyderabad 2026)
- Put [v-cloak] rule in global CSS (not scoped) — usually in index.html or main.css
- Combine with skeleton loaders — show nice placeholders instead of blank screen
- Use it on landing pages, pricing pages, login screens — places where first impression matters
- In Nuxt / SSR apps → you usually don’t need v-cloak because content is pre-rendered
- Test on slow 3G mobile connection → you’ll see why v-cloak is still important
Your mini practice task:
- Create a simple component with {{ user.name }} and {{ balance }}
- Open browser → throttle network to “Slow 3G”
- See the flash → add v-cloak + global CSS
- See clean content appear instantly after compile
Any part confusing? Want full examples for:
- v-cloak + skeleton loader on dashboard?
- v-cloak vs SSR (Nuxt) comparison?
- v-cloak + <Suspense> for async components?
- Real landing page with prices/names that never flashes raw {{ }}?
Just tell me — we’ll build the next clean, flash-free page together 🚀
