Vue v-show

v-show toggles an element's visibility using CSS display instead of adding or removing it from the DOM, making it well suited for content that toggles frequently.

What v-show Does

v-show always renders the element into the DOM. Instead of mounting and unmounting it like v-if, Vue simply toggles an inline style of display: none based on whether the bound expression is truthy or falsy. The element, and any component inside it, stays alive the whole time.

v-show vs v-if

The two directives look similar in templates but behave very differently underneath. v-if performs true conditional rendering — elements and their lifecycle hooks are created and destroyed on every switch. v-show mounts the element once and afterward only flips a CSS property, so lifecycle hooks like mounted() run a single time regardless of how many times visibility toggles.

Aspectv-ifv-show
Initial render costLow if condition starts falseAlways pays full render cost
Toggle costHigher — mounts/unmounts each timeLow — only a style flip
Supports v-elseYesNo
Works on <template>YesNo
Best forRarely-changing, permission-gated contentFrequently toggled UI (tabs, panels)
  • Prefer v-show for UI the user toggles often, like tabs, accordions, or modals opened repeatedly
  • Prefer v-if for content that is conditional on something that rarely changes, such as a user role
  • Prefer v-if when the condition being false should mean the content truly doesn't exist for accessibility or security purposes
  • v-show has no v-else counterpart — you must write the inverse condition on a second element if needed

A basic v-show toggle

const app = Vue.createApp({
  data() {
    return {
      isVisible: true
    }
  },
  methods: {
    toggle() {
      this.isVisible = !this.isVisible
    }
  }
})

app.mount('#app')

<!-- template -->
<div id="app">
  <button @click="toggle">Toggle message</button>
  <p v-show="isVisible">This paragraph stays in the DOM.</p>
</div>
Note: Choose v-show for toggle-heavy UI like tab panels or collapsible sections, where the extra one-time render cost is easily paid back by avoiding repeated mount/unmount work on every click.

Switching between tab panels

const app = Vue.createApp({
  data() {
    return {
      activeTab: 'profile'
    }
  }
})

app.mount('#app')

<!-- template -->
<div id="app">
  <button @click="activeTab = 'profile'">Profile</button>
  <button @click="activeTab = 'settings'">Settings</button>

  <div v-show="activeTab === 'profile'">Profile panel content</div>
  <div v-show="activeTab === 'settings'">Settings panel content</div>
</div>
Note: Content hidden with v-show is still fully present in the DOM and can still be found by page search, screen readers, or anyone inspecting the page. Never rely on v-show to hide sensitive data — use v-if, or don't send the data to the client at all.

v-show driven by a computed property

const app = Vue.createApp({
  data() {
    return {
      cartItemCount: 0
    }
  },
  computed: {
    hasItems() {
      return this.cartItemCount > 0
    }
  },
  methods: {
    addItem() {
      this.cartItemCount++
    }
  }
})

app.mount('#app')

<!-- template -->
<div id="app">
  <button @click="addItem">Add to cart</button>
  <p v-show="hasItems">You have {{ cartItemCount }} item(s) in your cart.</p>
</div>
Note: Combining v-show with a computed property, rather than a raw expression inline, keeps templates readable once the visibility logic involves more than one piece of state.

Exercise: Vue v-show

What mechanism does v-show use to hide an element?