Conditional Rendering
v-if
Use v-if to conditionally render an element based on an expression:
<p v-if="loggedIn">Welcome back!</p>The element and its children are only mounted when the expression is truthy. When it becomes falsy, the element is removed and its reactive scope is disposed.
v-else-if
Chain additional conditions with v-else-if:
<p v-if="status === 'loading'">Loading...</p>
<p v-else-if="status === 'error'">Something went wrong</p>
<p v-else-if="status === 'empty'">No results found</p>v-else-if must immediately follow a v-if or another v-else-if (whitespace and comments between them are allowed).
v-else
Use v-else as a fallback branch:
<p v-if="count > 0">You have {{ count }} items</p>
<p v-else>No items yet</p>v-else must be the last branch and takes no value.
Full example
<script setup>
import { signal, computed } from "decagrammaton";
const tasks = signal([]);
const status = computed(() => {
if (tasks.value.length === 0) return "empty";
if (tasks.value.every((t) => t.done)) return "complete";
return "active";
});
</script>
<template>
<div v-if="status === 'complete'">
<p>All done!</p>
</div>
<div v-else-if="status === 'active'">
<p>{{ tasks.filter((t) => !t.done).length }} tasks remaining</p>
</div>
<div v-else>
<p>No tasks yet. Add one!</p>
</div>
</template>How it works
The compiler folds a consecutive v-if / v-else-if / v-else run into a single conditional group. At runtime:
- The branch conditions are read inside a render effect.
- Each condition is evaluated in order; the first truthy branch is mounted.
- When the active branch changes, the old branch is unmounted (its scope disposed) and the new one is mounted at the same anchor.
Rules
v-ifandv-else-ifrequire an expression;v-elsetakes none.v-else-if/v-elsemust immediately follow av-iforv-else-if.- You can have multiple
v-else-ifbranches but only onev-else. - Each branch must have a single root element or component. A branch cannot be bare text or a fragment of multiple siblings.
v-ifandv-foron the same element is rejected (Vue givesv-ifhigher priority, so it couldn't see the loop variable — always a footgun). Wrap one in the other instead:
<!-- ✗ throws -->
<li v-for="task in tasks" v-if="task.done" :key="task.id">{{ task.text }}</li>
<!-- ok: nest instead -->
<div v-for="task in tasks" :key="task.id">
<span v-if="task.done">{{ task.text }}</span>
</div>No <template> grouping
Vue lets you put v-if on a <template> to toggle a group without a wrapper element. Decagrammaton does not support <template> as a grouping tag — use a real single root element (e.g. a <div>).