Skip to main content
Vue provides directives to conditionally render elements based on reactive state.

v-if Directive

The v-if directive conditionally renders an element based on the truthiness of an expression:
<template>
  <div>
    <p v-if="isVisible">This is visible</p>
    <button @click="isVisible = !isVisible">Toggle</button>
  </div>
</template>

<script setup>
import { ref } from 'vue'

const isVisible = ref(true)
</script>
When v-if is false, the element and its children are not rendered in the DOM at all.

v-else Directive

The v-else directive provides an “else block” for v-if:
<template>
  <div>
    <p v-if="isLoggedIn">Welcome back!</p>
    <p v-else>Please log in</p>
  </div>
</template>

<script setup>
import { ref } from 'vue'

const isLoggedIn = ref(false)
</script>
A v-else element must immediately follow a v-if or v-else-if element - otherwise it won’t be recognized.

v-else-if Directive

The v-else-if serves as an “else if block” for v-if:
<template>
  <div>
    <p v-if="type === 'A'">Type A</p>
    <p v-else-if="type === 'B'">Type B</p>
    <p v-else-if="type === 'C'">Type C</p>
    <p v-else>Not A/B/C</p>
  </div>
</template>

<script setup>
import { ref } from 'vue'

const type = ref('A')
</script>
Chain multiple v-else-if blocks to create complex conditional rendering logic.

v-if on template

Because v-if is a directive, it must be attached to a single element. To conditionally render multiple elements, use v-if on a <template> element:
<template>
  <div>
    <template v-if="showDetails">
      <h3>User Details</h3>
      <p>Name: John Doe</p>
      <p>Email: [email protected]</p>
      <p>Role: Admin</p>
    </template>
  </div>
</template>

<script setup>
import { ref } from 'vue'

const showDetails = ref(true)
</script>
The <template> element serves as an invisible wrapper and won’t be rendered in the final DOM.

v-show Directive

The v-show directive is an alternative to v-if for conditionally displaying an element:
<template>
  <div>
    <p v-show="isVisible">Toggle me</p>
    <button @click="isVisible = !isVisible">Toggle</button>
  </div>
</template>

<script setup>
import { ref } from 'vue'

const isVisible = ref(true)
</script>

v-show Implementation

From packages/runtime-dom/src/directives/vShow.ts, v-show toggles the display CSS property:
export const vShow: ObjectDirective<VShowElement> = {
  beforeMount(el, { value }, { transition }) {
    el[vShowOriginalDisplay] = el.style.display === 'none' ? '' : el.style.display
    if (transition && value) {
      transition.beforeEnter(el)
    } else {
      setDisplay(el, value)
    }
  },
  // ...
}

function setDisplay(el: VShowElement, value: unknown): void {
  el.style.display = value ? el[vShowOriginalDisplay] : 'none'
}
When using v-show, the element is always rendered in the DOM. Only the CSS display property is toggled.

v-if vs v-show

<template>
  <!-- Element is added/removed from DOM -->
  <div v-if="isVisible">
    <ExpensiveComponent />
  </div>
</template>

<script setup>
import { ref } from 'vue'

const isVisible = ref(false)
// ExpensiveComponent is not mounted until isVisible is true
</script>

When to Use Which

1

Use v-if when:

  • The condition rarely changes
  • You want true conditional rendering (lazy)
  • The content is expensive to render
  • You want to leverage conditional component lifecycle hooks
2

Use v-show when:

  • The condition changes frequently
  • You need better toggle performance
  • The element is simple and lightweight
  • Initial render cost is more important than toggle cost
Performance comparison:
  • v-if has higher toggle cost (element creation/destruction)
  • v-if has lower initial render cost (lazy)
  • v-show has higher initial render cost (always rendered)
  • v-show has lower toggle cost (CSS only)

v-if with v-for

Using v-if and v-for on the same element is not recommended. When used together on the same element, v-if has higher priority than v-for.
Instead, use a wrapper element:
<template>
  <!-- Bad: v-if and v-for on same element -->
  <li v-for="item in items" v-if="item.isActive" :key="item.id">
    {{ item.name }}
  </li>

  <!-- Good: Use template wrapper -->
  <template v-for="item in items" :key="item.id">
    <li v-if="item.isActive">
      {{ item.name }}
    </li>
  </template>

  <!-- Better: Use computed property -->
  <li v-for="item in activeItems" :key="item.id">
    {{ item.name }}
  </li>
</template>

<script setup>
import { ref, computed } from 'vue'

const items = ref([
  { id: 1, name: 'Item 1', isActive: true },
  { id: 2, name: 'Item 2', isActive: false },
  { id: 3, name: 'Item 3', isActive: true }
])

const activeItems = computed(() => 
  items.value.filter(item => item.isActive)
)
</script>

Conditional Component Rendering

You can conditionally render entire components:
<template>
  <div>
    <component :is="currentView" />
    
    <button @click="currentView = 'ComponentA'">Show A</button>
    <button @click="currentView = 'ComponentB'">Show B</button>
  </div>
</template>

<script setup>
import { ref } from 'vue'
import ComponentA from './ComponentA.vue'
import ComponentB from './ComponentB.vue'

const currentView = ref('ComponentA')
</script>
Or use v-if:
<template>
  <div>
    <ComponentA v-if="view === 'A'" />
    <ComponentB v-else-if="view === 'B'" />
    <ComponentC v-else />
  </div>
</template>

Conditional Rendering with Transitions

Combine conditional rendering with Vue’s transition system:
<template>
  <div>
    <button @click="show = !show">Toggle</button>
    
    <Transition name="fade">
      <p v-if="show">Hello!</p>
    </Transition>
  </div>
</template>

<script setup>
import { ref } from 'vue'

const show = ref(true)
</script>

<style scoped>
.fade-enter-active,
.fade-leave-active {
  transition: opacity 0.5s ease;
}

.fade-enter-from,
.fade-leave-to {
  opacity: 0;
}
</style>

Complex Conditional Logic

For complex conditions, use computed properties:
<template>
  <div>
    <div v-if="canEditPost">
      <button>Edit</button>
      <button>Delete</button>
    </div>
    
    <p v-if="errorMessage" class="error">
      {{ errorMessage }}
    </p>
  </div>
</template>

<script setup>
import { ref, computed } from 'vue'

const user = ref({ role: 'admin', id: 1 })
const post = ref({ authorId: 1, status: 'published' })

const canEditPost = computed(() => {
  return user.value.role === 'admin' || 
         user.value.id === post.value.authorId
})

const errorMessage = computed(() => {
  if (!user.value) return 'Please log in'
  if (post.value.status === 'archived') return 'Cannot edit archived posts'
  return null
})
</script>

Key Attribute with Conditional Rendering

Use the key attribute to force Vue to replace elements instead of reusing them:
<template>
  <div>
    <label>
      <input v-if="loginType === 'username'" key="username" placeholder="Username" />
      <input v-else key="email" placeholder="Email" />
    </label>
    
    <button @click="toggleLoginType">Toggle</button>
  </div>
</template>

<script setup>
import { ref } from 'vue'

const loginType = ref('username')

function toggleLoginType() {
  loginType.value = loginType.value === 'username' ? 'email' : 'username'
}
</script>
Without the key attribute, Vue would reuse the input element and only update its attributes. With key, Vue creates a new element each time.

List Rendering

Render lists with v-for directive

Template Syntax

Learn Vue’s template syntax

Computed Properties

Use computed for complex conditions

Build docs developers (and LLMs) love