Tutorials Logic, IN info@tutorialslogic.com

Vue Conditional Rendering v if, v show, is

Branch Lifecycle

v-if creates and destroys a branch according to a condition. v-show keeps the element rendered and toggles CSS display. Choose v-if for infrequent or expensive branches and v-show for frequently toggled simple content, then measure when the subtree is substantial.

v-if vs v-show

Vue provides two ways to conditionally show elements. Choose based on how often the condition changes:

Feature v-if v-show
DOM presence Removed/added from DOM Always in DOM (display:none)
Initial render cost Lower (if false) Higher (always renders)
Toggle cost Higher (destroy/create) Lower (CSS only)
Works with v-else Yes No
Best for Rarely toggled conditions Frequently toggled visibility

v-if, v-else-if, v-else, v-show, Dynamic Components

v-if, v-else-if, v-else, v-show, Dynamic Components
<template>
  <div>
    <!-- v-if / v-else-if / v-else -->
    <div v-if="status === 'loading'">
      <span class="spinner"></span> Loading...
    </div>
    <div v-else-if="status === 'error'">
      <p class="error">{{ errorMessage }}</p>
      <button @click="retry">Retry</button>
    </div>
    <div v-else-if="status === 'empty'">
      <p>No data found.</p>
    </div>
    <div v-else>
      <!-- data is ready -->
      <ul>
        <li v-for="item in items" :key="item.id">{{ item.name }}</li>
      </ul>
    </div>

    <!-- v-show - stays in DOM, toggles display -->
    <div v-show="isMenuOpen" class="dropdown-menu">
      <a href="#">Profile</a>
      <a href="#">Settings</a>
      <a href="#">Logout</a>
    </div>
    <button @click="isMenuOpen = !isMenuOpen">Menu</button>

    <!-- <template> with v-if - no extra DOM element -->
    <template v-if="isAdmin">
      <h3>Admin Section</h3>
      <p>Only admins see this.</p>
      <button>Manage Users</button>
    </template>

    <!-- Conditional class/style -->
    <button
      :class="['btn', isActive ? 'btn-primary' : 'btn-secondary']"
      :disabled="isLoading"
    >
      {{ isLoading ? 'Saving...' : 'Save' }}
    </button>
  </div>
</template>

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

const status = ref('loading')  // 'loading' | 'error' | 'empty' | 'success'
const errorMessage = ref('Failed to fetch data')
const items = ref([{ id: 1, name: 'Item 1' }, { id: 2, name: 'Item 2' }])
const isMenuOpen = ref(false)
const isAdmin = ref(true)
const isActive = ref(true)
const isLoading = ref(false)

function retry() { status.value = 'loading' }
</script>

v-if vs v-show - HTML Example

v-if vs v-show - HTML Example
<!-- Dynamic Components - <component :is="..."> -->
<template>
  <div>
    <!-- Tab navigation -->
    <div class="tabs">
      <button
        v-for="tab in tabs"
        :key="tab.name"
        @click="currentTab = tab.name"
        :class="{ active: currentTab === tab.name }"
      >
        {{ tab.label }}
      </button>
    </div>

    <!-- Dynamic component - renders the active tab component -->
    <component :is="currentTabComponent" v-bind="tabProps" />

    <!-- KeepAlive - cache inactive components (preserve state) -->
    <KeepAlive :include="['HomeTab', 'ProfileTab']" :max="3">
      <component :is="currentTabComponent" />
    </KeepAlive>

    <!-- Dynamic component with string name (globally registered) -->
    <component :is="'BaseButton'" label="Click me" />

    <!-- Conditional component rendering -->
    <component
      :is="user.role === 'admin' ? AdminDashboard : UserDashboard"
      :user="user"
    />
  </div>
</template>

<script setup>
import { ref, computed } from 'vue'
import HomeTab    from './tabs/HomeTab.vue'
import ProfileTab from './tabs/ProfileTab.vue'
import SettingsTab from './tabs/SettingsTab.vue'
import AdminDashboard from './AdminDashboard.vue'
import UserDashboard  from './UserDashboard.vue'

const currentTab = ref('home')
const user = ref({ role: 'admin', name: 'Alice' })

const tabs = [
  { name: 'home',     label: 'Home',     component: HomeTab },
  { name: 'profile',  label: 'Profile',  component: ProfileTab },
  { name: 'settings', label: 'Settings', component: SettingsTab },
]

const currentTabComponent = computed(() =>
  tabs.find(t => t.name === currentTab.value)?.component
)

const tabProps = computed(() => ({
  userId: 1,
  // other shared props
}))
</script>

State Across Branches

Destroying a branch also unmounts child components and releases their local state. That is useful for a fresh dialog, but surprising for an unfinished form. Move draft state to an appropriate owner or keep the component alive deliberately when the user expects continuity.

Use one v-if / v-else-if / v-else chain for mutually exclusive states such as loading, error, empty, and success. Keep the branches adjacent and avoid conditions with hidden side effects. A computed value makes a complex condition easier to name and test.

Before you move on

Vue Conditional Rendering v if, v show, is Mastery Check

5 checks
  • DOM presence means Removed/added from DOM; a typical example is Always in DOM (display:none).
  • Initial render cost means Lower (if false); a typical example is Higher (always renders).
  • Toggle cost means Higher (destroy/create); a typical example is Lower (CSS only).
  • Works with v-else means Yes; a typical example is No.
  • v-if vs v-show includes Removed/added from DOM, Lower (if false), Higher (destroy/create), and Yes.
Browse Free Tutorials

Explore 500+ free tutorials across 20+ languages and frameworks.